1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-omr

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
CodingStandard.md 52 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 29.11.2024 01:31 668fed1

Стандарты кодирования OMR для C и C++

Принципы

  • Весь код должен быть читаемым. Некоторые рекомендации могут потребовать немного больше работы (набора текста) при написании кода, но в результате получится код, который легче читать.
  • Не пишите код, чтобы он был умным. Используйте свой ум для разработки умных алгоритмов.
  • Делайте код простым. Используйте короткие «предложения», понятный, однозначный «словарь» и хорошо организованные «абзацы». Старайтесь делать только одно действие в каждой строке кода.
  • Комментарии используются для объяснения цели кода, а не того, как он работает. Хорошо написанный код не должен требовать комментариев, объясняющих, как он работает. Комментарии должны объяснять, почему это необходимо.
  • OMR разработан так, чтобы быть переносимым. Избегайте использования передовых языковых функций, которые ограничивают переносимость кода.

Применение правил

Стандарты кодирования OMR со временем развивались, и, подобно исходному коду, мы ожидаем, что они продолжат улучшаться. В результате старый исходный код может не полностью соответствовать самой последней версии стандартов. Тем не менее, как сообщество, наша цель — следовать стандартам, насколько это возможно. По мере обновления старого кода мы стремимся приблизиться к стандартам. Участники обязуются соблюдать правила во время проверки кода.

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

Новый код

  • Все новые функции и файлы должны соответствовать правилам.

Старый код

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

Здравый смысл

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

Стандарты для кода на C и C++

Правила ISO/ANSI
  • Весь код на C написан в соответствии с правилами для компиляторов C99.
  • Нет необходимости ориентироваться на компиляторы до ANSI (K&R), равно как и полагаться на наличие функций C++11.
  • Нет необходимости учитывать правило ANSI об уникальных первых 6 символах имён.
  • Функции, не принимающие аргументов, объявляются и определяются с использованием void в списке параметров. Правильно:
int
myFunction(void)
{
    statements;
}

Неправильно:

int
myFunction()
{
    statements;
}

Обоснование:

  • Существуют классы ошибок, возникающих при смешивании стилей K&R и ANSI.
  • Разрешение использования компиляторов, отличных от ANSI, усложняет код. Компиляторы ANSI сейчас широко распространены.
  • Прототипирование выявляет некоторые ошибки; с другой стороны, отсутствие прототипов может вызвать некоторые ошибки.
  • Пустой список параметров вызывает предупреждения на некоторых компиляторах и снижает безопасность типов, что позволяет выявить некоторые классы ошибок.
Стиль фигурных скобок
  • Стиль фигурных скобок — OTBS (One True Brace Style) (также известный как стиль K&R или стиль Джона Траволты). Открытие фигурных скобок функции должно быть в первом столбце.

Правильно:

int
myFunction(void) {
    statements;
}

Неправильно:

int
myFunction(void)
{
    statements;
}

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

Лучше:

if (
#ifdef FOO
    condition1
#else
    condition2
#endif
) {
    statements;
}

Правильно:

#ifdef FOO
if (condition1) {
#else
if (condition2) {
#endif
    statements;
}

Неправильно:

#ifdef FOO
if (condition1)
#else
if (condition2)
#endif
{
    statements;
}

Все операторы if, for, while, do и другие управляющие операторы должны быть записаны с фигурными скобками.

Правильно:

if (foo) {
    doSomething();
}

Неправильно:

if (foo) doSomething();

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

Общий стиль фигурных скобок позволяет:

  • Последовательный стиль приводит к более быстрому чтению и написанию кода.
  • Совпадающие фигурные скобки облегчают точное определение блоков.
  • Размещение однострочных операторов условия или цикла на одной строке с управляющим оператором делает невозможным установку точки останова на условном операторе.

Отступ кода с помощью ТАБУЛЯЦИЙ (не пробелов).

  • Используйте табуляции для отступа.
  • Табуляция обычно устанавливается равной 4 пробелам.

Форматирование операторов switch.

  • Метки switch (case или default) имеют отступ на том же уровне, что и ключевое слово switch.

Правильно:

switch (foo) {
case 1:
    doCaseOne();
    break;
case 2:
    doCaseTwo();
    break;
default:
    doDefaultCase();
    break;
}

Неправильно:

switch (foo) {
    case 1:
        doCaseOne();
        break;
    case 2:
        doCaseTwo();
        break;
    default:
        doDefaultCase();
        break;
}
  • Операторы switch всегда должны включать метку default, хотя бы для утверждения сбоя.

Правильно:

switch (foo) {
case 1:
    doCaseOne();
    break;
case 2:
    doCaseTwo();
    break;
default:
    assertUnreachable();
}

Неправильно:

switch (foo) {
case 1:
    doCaseOne();
    break;
case 2:
    doCaseTwo();
    break;
}
  • Размещайте операторы break на отдельных строках (выполняйте только одну операцию в каждой строке).
  • Избегайте провала, если это возможно.
  • При использовании провала используйте метку /* FALLTHROUGH */.
  • Допускается исключение из правила провала для меток без кода.

Лучше (без провала):

switch (foo) {
case 1:
    doCaseOne();
    break;
case 2:
    doCaseTwo();
    doCaseThree();
    break;
case 3:
    doCaseThree();
    break;
case 4:
case 5:
    doCaseFourOrFive();
    break;
default:
    assertUnreachable();
}

Правильно:

switch (foo) {
case 1:
    doCaseOne();
    break;
case 2:
    doCaseTwo();
    /* FALLTHROUGH */
case 3:
    doCaseThree();
    break;
case 4:
case 5:
    doCaseFourOrFive();
    break;
default:
    assertUnreachable();
}

Неправильно:

switch (foo) {
case 1:
    doCaseOne(); break;
case 2:
    doCaseTwo();
case 3:
    doCaseThree(); break;
case 4:
case 5:
    doCaseFourOrFive(); break;
}

Обоснование.

  • Отступ меток case и кода под ними приводит к чрезмерному горизонтальному интервалу.
  • Провал является необычным и неожиданным; избегайте его или, по крайней мере, явно указывайте на него.
  • Инструменты, такие как Lint, распознают комментарий FALLTHROUGH.
  • Метка default с утверждениями помогает обнаруживать и диагностировать ошибки.

Форматирование условий.

  • Когда сравниваете lvalue и rvalue на равенство или неравенство, всегда ставьте rvalue слева.

Правильно:

if (0 == value1) {
    ...
} else if (0 != value2) {
    ...
} else if ((value4 + 1) == value3) {
    ...
}

Неправильно:

if (value1 == 0) {
    ...
} else if (value2 != 0) {
    ...
} else if (value3 == (value4 + 1)) {
    ...
}
  • Когда сравниваете результат функции с константой, всегда ставьте константу слева.

Правильно:

if (0 == helperFunction(count, count + 1, dataBlock)) {
    ...
}

Неправильно:

if (helperFunction(count, count + 1, dataBlock) == 0) {
    ...
}
  • Старайтесь избегать длинных условий. Можно ли переписать условие, чтобы оно было проще?

  • Когда... Неизбежно, форматируйте длинные условия с помощью «&&» или «||» в начале каждой строки и заканчивая «) {» на отдельной строке.

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

Правильно:

if ((value1 == option1)
    && ((value2a == option2) || (value2b == option2))
    && (value3 == option3)
) {
    ...
}

Неправильно:

if ((value1 == option1) &&
    ((value2a == option2) ||  (value2b == option2)) &&
    (value3 == option3)) {
    ...
}

Никогда не объединяйте присваивание и проверку, за исключением цикла while. В цикле while всегда используйте явную проверку.

Правильно:

myPointer = allocateStructure();
if (NULL != myPointer) {
    ...
}

Неправильно:

if (myPointer = allocateStructure()) {
    ...
}

Правильно:

while (NULL != (cursor = getNext(iterator))) {
    ...
}

Неправильно:

while (cursor = getNext(iterator)) {
    ...
}

По возможности старайтесь избегать отрицательных условий. Положительные условия легче понять.

Правильно:

if (NULL == myPointer) {
    doAnotherThing();
} else {
    doOneThing();
}

Неправильно:

if (NULL != myPointer) {
    doOneThing();
} else {
    doAnotherThing();
}

Используйте следующий формат для сравнений (<, <=, >, или >=) вида (min <= value <= max), чтобы улучшить ясность.

Правильно:

if ((0 <= logLevelValue) && (logLevelValue <= 4)) {

Неправильно:

if ((logLevelValue >= 0) && (logLevelValue <= 4)) {

Обоснование

  • Размещение rvalues слева может предотвратить случайное присваивание (= vs ==).
  • Размещение небольших констант слева улучшает читаемость.
  • Длинные цепочки условной логики могут быть трудны для понимания. Избегание этого шаблона улучшает читаемость.
  • Начало каждой строки с «||» или «&&» обеспечивает визуальную согласованность, поскольку соединяющие операторы выровнены.
  • Последовательность «)» {» даёт чёткое визуальное указание на то, что условие закончилось и блок начался.
  • Правила порядка операций в C сложны; избегайте путаницы и ошибок, используя круглые скобки для явного указания порядка.
  • Объединение присваивания и проверок нарушает принцип «Старайтесь делать только одно действие в каждой строке кода».

Передача параметров

Все объекты и структуры должны передаваться по адресу (*), а не по ссылке или значению. Другие параметры, требующие обновления, также должны передаваться по адресу. Необъективные параметры, которые не обновляются, должны передаваться по значению. Аргументы C++ по ссылке (&) использовать нельзя. Параметры-указатели должны быть документированы с [in], [out] или [in,out], чтобы различать входные и выходные параметры.

Правильно:

void
myFunction(MyObject *arg);

Неправильно:

void
myFunction(MyObject& arg);

В качестве исключения из правила очень простые объекты C++ (например, базовые типы-обёртки) могут передаваться по значению.

Правильно:

void
myFunction(MyFlags arg);

Обоснование

  • Аргументы-ссылки могут вызывать проблемы. Например, невозможно передать NULL для ссылочного объекта. Они просто синтаксический сахар, скрывающий указатели, поэтому мы предпочитаем явно указывать способ передачи аргумента.

Макросы

Макросы должны быть короткими и консервативными. Если макрос занимает более 2–3 строк, рассмотрите возможность написания его в виде подпрограммы. Макросы должны быть «замкнутыми» и «независимыми от контекста». Не полагайтесь на знание имён переменных, определённых в контексте, в котором используется макрос. Если такое знание необходимо, используйте установочный макрос для объявления переменной (например, OMRPORT_ACCESS_FROM_OMRPORT()).

Правильно:

#define FOO(v, x) ((v) = (x))

Неправильно:

#define FOO(x) ((v) = (x))

Не полагайтесь на рекурсивную оценку макросов. Хотя некоторые компиляторы поддерживают это, они не строго соответствуют спецификации ISO. Защищайте аргументы в макросах круглыми скобками.

Правильно:

#define FOO(x) ((x) + 1)

Неправильно:

#define FOO(x) x+1

Если макросу требуется область видимости, используйте do {} while (0) вместо {}. Это позволяет избежать нескольких трудно диагностируемых ошибок.

Правильно:

#define FOO(x) \
    do { \
        int32_t y = x; \
        ... \
    } while (0)

Неправильно:

#define
``` **Использование макросов в C**

* Для многострочных макросов всегда начинайте тело макроса с новой строки.

Правильно:
```c
#define FOO(x) \
    { \
        int32_t y = x; \
        ... \
    }

Неправильно:

#define FOO(x) do { \
        ... \
    } while (0)
  • Избегайте глубокой вложенности #ifdefs. Вместо этого используйте #elif.

Правильно:

#if defined(FOO)
    return "foo";
#elif defined(BAR)
    return "bar";
#else
    return "other";
#endif

Неправильно:

#ifdef FOO
    return "foo";
#else
#ifdef BAR
    return "bar";
#else
    return "other";
#endif
#endif
  • Используйте функциональные макросы, за исключением определения констант. Любой макрос, который «что-то делает», должен выглядеть как функция.

Правильно:

#define DOIT() doSomething()
#define MAX 23

Неправильно:

#define DOIT doSomething()
#define MAX() 23

Также см. Комментарий к директивам условной компиляции (if, #ifdef, #ifndef, else, elif, endif).

Обоснование

  • Использование do {} while(0) заставляет пользователей макроса следовать за ним точкой с запятой, что делает его более похожим на вызов функции.
  • Когда за макросом следует точка с запятой, он образует одно (составное) выражение, в отличие от блока, за которым следует точка с запятой, которое представляет собой два выражения.
  • Невозможно случайно использовать макрос в качестве тела функции.

Возврат

  • Не заключайте возвращаемое значение в круглые скобки.

Правильно:

return 0;

Неправильно:

return (0);
  • Избегайте множественных точек возврата.

Правильно:

int32_t result = 0;
if (NULL != input) {
    result = input->size;
}
return result;

Неправильно:

if (NULL == input) {
    return 0;
} else {
    return input->size;
}

Обоснование: return — это ключевое слово, а не функция. return — это неструктурированный переход, как goto. Это может затруднить понимание потока кода.

Заголовочные файлы

  • Заголовочные файлы всегда должны быть заключены в #ifdefs, чтобы предотвратить их включение более одного раза.

Правильно:

#if !defined(FILENAME_HPP_)
#define FILENAME_HPP_
. . .
#endif /* !defined(FILENAME_HPP_) */
  • При включении заголовочного файла обязательно используйте правильный стиль директивы include. Используйте <угловые скобки> для системных заголовочных файлов и «двойные кавычки» для заголовочных файлов OMR.

Правильно:

#include <string.h>

Неправильно:

#include "string.h"

Правильно:

#include "omr.h"

Неправильно:

#include <omr.h>
  • Организуйте заголовочные файлы в четыре группы:
    1. «соответствующий заголовочный файл» — заголовочный файл, объявляющий модуль или класс;
    2. системные заголовочные файлы — string.h, math.h и т. д.;
    3. заголовочные файлы OMR C — любой файл .h в OMR;
    4. заголовочные файлы C++ OMR — любой файл .hpp в OMR.
  • Сортируйте заголовки в алфавитном порядке внутри каждой группы.
  • Не создавайте зависимости упорядочения в заголовочных файлах.

Правильно:

#include "ThisClass.hpp"

#include <float.h>
#include <string.h>

#include "omr.h"

#include "AnotherClass.hpp"
#include "HelperClass.hpp"
#include "OtherClass.hpp"

Обоснование: правильно защищённые заголовочные файлы предотвращают ошибки переопределения. Сортировка заголовков в алфавитном порядке помогает избежать случайного дублирования включений.

Указатели на константы

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

Правильно:

void
doSomething(const SomeOptions *options)
{
    const uint32_t optionsCount = options->count;
    uint32_t optionIndex = 0;
    for (; optionIndex < optionsCount; optionIndex++) {
        ...

Обоснование: const устанавливает официальный договор между вызывающим и вызываемым. Компилятор может проверить соблюдение этого договора. Маркировка временных переменных const помогает улучшить читаемость, явно различая истинные переменные и фиксированные значения. Использование const может позволить большую оптимизацию.

Объявления переменных

  • Никогда не объявляйте более одной переменной в строке.
  • Назначьте начальное значение каждой переменной. Объяснение чего-либо
    запутанное
    */

...
}


* Не следует использовать комментарии для отключения части кода. Вместо этого используйте #if 0/#endif.

Правильно:
```c
#if 0
   printf("Returning %d\n", rc);
#endif

Неправильно:

/* printf("Returning %d\n", rc);
*/

Обоснование

  • // комментарии не допускаются в C89.
  • Избегать их в коде на C++ — хорошая практика, так как это упрощает переключение между C и C++.
  • Если начинать каждую строку комментария с *, комментарий будет выделяться среди реального кода, особенно при использовании редактора без подсветки синтаксиса.
  • Размещение закрывающего */ на отдельной строке упрощает определение конца комментария. Это также позволяет убедиться, что комментарий завершён правильно.
  • Использование #if 0 отличает реальные комментарии от неработающего кода. Также оно позволяет вкладывать код, содержащий комментарии.

Комментарий к параметрам функции должен быть понятным

  • Объявление функции предваряется стратегическим комментарием.
  • Комментарии к функциям находятся в заголовочных файлах для нестатических функций. Не дублируйте комментарий в файле C.
  • Описание замысла функции должно быть таким, чтобы читатель мог легко переписать функцию, если исходный код недоступен.
  • Для комментариев к функциям используется формат Doxygen.
  • Опишите все параметры и возвращаемые значения.
  • Всегда указывайте единицы измерения или тип количества (например, байты, мегабайты, проценты, соотношение).
  • Основная цель комментирования параметров — документировать обязанности вызывающей стороны (например, необходимость освобождения памяти в какой-то момент).
  • Если функция использует или изменяет глобальные данные, это документируется в комментарии к функции.
  • Делайте комментарии простыми и понятными.

Правильно:

/**
 * Определить количество виджетов, необходимое для изготовления
 * желаемого количества гаджетов.
 * @param[in] gadgetFactory фабрика гаджетов
 * @param[in] gadgetsRequired требуемое количество гаджетов
 * @return килограммы необходимых виджетов
 */
uint32_t
getWidgetsRequired(GadgetFactory *gadgetFactory, uint32_t gadgetsRequired)
{
    ...

Обоснование

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

Явно комментируйте элементы структуры, класса или объединения

  • Каждый элемент структуры должен иметь комментарий Doxygen, объясняющий его использование.
  • Всегда указывайте единицы измерения или тип количества, где это уместно (например, байты, мегабайты, проценты, соотношения).

Правильно:

typedef struct WidgetData {
    uint32_t widgetSize; /**< Размер каждого виджета в байтах */
    float widgetRate; /**< Скорость доставки виджетов в виджетах в миллисекунду */
} WidgetData;

Комментируйте директивы условной компиляции (#if, #ifdef, #ifndef, #else, #elif, #endif)

  • Каждая директива условной компиляции должна документировать условие, которое она закрывает.
  • #endif должен иметь комментарий, описывающий соответствующий #if, #ifdef или #ifndef, где это возможно.
  • #elif и #else должны иметь комментарии, повторяющие предыдущее условие.

Правильно:

#if defined(__cplusplus)
...
#endif /* defined(__cplusplus) */

Неверно:

#if defined (__cplusplus)
...
#endif

Правильно:

#if defined(A) || defined(B)
...
#elif !defined(C) /* defined(A) || defined(B) */
...
#else /* !defined(C) */
...
#endif /* defined(A) || defined(B) */

Правильно:

#if defined(A)
...
#else /* defined(A) */
...
#endif /* defined(A) */

Неверно:

#if defined(A)
...
#else /* !defined(A) */
...
#endif /* !defined(A) */

Соглашения об именах

Заголовочные файлы

  • Файлы заголовков классов имеют то же имя, что и класс. Другие имена заголовков написаны строчными буквами.
  • Префиксы (например, MM_) опускаются.

Модули

  • Имена модулей написаны строчными буквами.

Классы

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

Имена классов пишутся в стиле «верблюжий регистр», например, WorkPackets.

Все классы должны иметь отдельные файлы .cpp и .hpp.

Файл .cpp можно опустить, если он будет пустым.

Функции и методы

  • Имена функций начинаются со строчной буквы.
  • Имена функций пишутся в стиле «верблюжий регистр»: например, getWidgetFactory().
  • Акронимы полностью пишутся прописными буквами, кроме случаев, когда они стоят в начале, тогда они полностью пишутся строчными: например, restartGC(), gcHelperFunction().
  • В именах методов не используются символы подчёркивания.
  • Именование функций может использовать символы подчёркивания для указания на «пространство имён», где такая конвенция уже была установлена: например, omrpool_startDo().
  • Функции, возвращающие булевы значения, называются в соответствии с истинным значением булевого выражения таким образом, чтобы это было абсолютно однозначно.

Пример на C++

Правильно:

bool isValid(void);

if (isValid()) {
    doit();
}

Неправильно:

bool checkValid(void);

if (checkValid()) {
    doit();
}

Пример на языке C

Правильно:

BOOLEAN isValid(void);

if (isValid()) {
    doit();
}

Неправильно:

BOOLEAN checkValid(void);

if (checkValid()) {
    doit();
}

Переменные

  • Переменные начинаются со строчных букв.
  • Переменные пишутся в стиле «верблюжий регистр»: например, heapSize.
  • Для переменных-членов имена начинаются с символа подчёркивания: например, _heapSize (только для C++).
  • Акронимы пишутся полностью прописными, кроме случаев, когда они стоят в начале, тогда они пишутся полностью строчными.
  • Обратите внимание, что все идентификаторы, начинающиеся с символа подчёркивания, за которым следует заглавная буква, зарезервированы для компилятора и не должны использоваться в коде OMR. Например, _S нельзя использовать.
  • Названия выбираются так, чтобы они были читаемыми, но не слишком длинными (исключения для индексов циклов), чтобы передать наилучшую информацию о данных или функциональности.

Макросы

  • Макросы должны быть написаны прописными буквами.

Правильно:

#define INVARIANT_SNIPPET_LENGTH 18

Обоснование:

  • Названия влияют на то, как вы думаете о чём-то. Хорошо подобранные названия могут означать разницу между мгновенным пониманием и путаницей.
  • Полезно, чтобы разные синтаксические элементы назывались по-разному, для мгновенного распознавания при чтении кода и, следовательно, более быстрого программирования.

Аббревиатуры

  • Не сокращайте слова, если аббревиатура широко не используется в отрасли. Например, используйте «resolve» вместо «reslv».
  • Это правило применяется ко всем именам, включая структуры, функции и временные переменные.

Используйте typedef для идентификации структур

  • typedef всегда используется при объявлении перечисления, структуры и объединения в C.
  • C++ не требует typedef для этих элементов.
  • Всегда используйте одно и то же имя для struct и typedef.
  • Не используйте typedef, чтобы определить указатель на структуру.
  • Вот пример, показывающий правильно помеченную структуру, и ту же структуру с отсутствующим тегом:

Правильно:

typedef struct MyStruct {
    char *string; /**<...
    uint32_t value; /**<...
} MyStruct;

Неверно:

typedef struct {
    char *string; /**<...
    uint32_t value; /**<...
} MyStruct, *MyStructp;

Неверно:

typedef struct MyStructTag {
    char *string; /**<...
    uint32_t value; /**<...
} MyStruct;

Обоснование:

  • Typedefs позволяют писать более лаконичный код, избегая повторяющегося использования префиксов enum, struct или union.
  • Указатели typedefs излишне скрывают, что используется указатель. Имя, типизированное с помощью typedef, легко превратить в указатель, добавив простую звёздочку.

Правила OMR

Типы данных

  • Предпочитайте использовать стандартные типы данных фиксированной ширины, такие как uint8_t, int8_t, uint32_t и т. д. Следует избегать встроенных типов, таких как int и long. Они могут иметь разный размер на разных платформах.

  • Исключения могут быть сделаны при работе со стандартными библиотечными или специфическими для платформы функциями. В этом случае используйте соответствующие типы данных C, такие как char или size_t или DWORD.

  • Также допускается исключение для указателей на строки C. Они должны быть char * (или const char *), а не uint8_t *.

  • Используйте double для данных с плавающей запятой, если нет веской причины использовать другой тип. Обоснование

  • Типы данных C различаются по размеру и знаку в зависимости от платформы. Использование чётко определённых типов позволяет избежать проблем с переносимостью в будущем.

Функции библиотеки

  • Код OMR не должен полагаться ни на что, кроме самых основных стандартных библиотечных функций.
  • Как правило, следует ограничиться функциями, объявленными в <string.h>, <stdarg.h> и <math.h>. <ctype.h> следует использовать с осторожностью, см. ниже.
  • Не следует использовать sprintf(). Вместо этого используйте функцию omrstr_printf() из библиотеки port.
  • Не следует использовать printf(). Вместо этого используйте функцию omrtty_printf() из библиотеки port.
  • Не следует использовать stricmp(). Это нестандартная функция.
  • Не следует использовать strdup(). Это нестандартная функция.
  • Не следует использовать malloc(), calloc(), realloc(), alloca() и free(). Вместо них используйте процедуры управления памятью из библиотеки port.
  • Не следует использовать tolower(), toupper(), strcasecmp() и strncasecmp(), поскольку они дают результаты, зависящие от локали и платформы, которые могут быть неожиданными. Вместо них используйте j9_cmdla_tolower(), j9_ascii_tolower(), j9_cmdla_toupper(), j9_ascii_toupper(), j9_cmdla_stricmp() и j9_cmdla_strnicmp().
  • Это правило не распространяется на платформенно-зависимый код (особенно в библиотеках port и thread).
  • Если вы сомневаетесь, поищите другой код в источнике OMR, который использует нужную вам функцию. Если вы обнаружите более одного другого использования в общем коде, скорее всего, он переносим. Если вы встретите только одно использование, возможно, нам следует заменить это использование на аналогичный вызов.

Обоснование

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

Предупреждения и ошибки

  • C и C++ код OMR должен компилироваться без предупреждений или ошибок.
  • Не просто скрывайте предупреждение приведением типа. Поймите, что означает ошибка, и исправьте свой код соответствующим образом (используя приведение типа, если это уместно).
  • Некоторые распространённые ошибки, которые могут не диагностироваться на некоторых платформах, включают:
    • присваивание rvalue. Например, (uint32_t *)x = y;
    • сдвиг более чем на количество битов в типе. Например, 1 << 36 /* undefined if sizeof(int) <= 4 */. Используйте ((uint64_t)1) << 36 вместо этого.

Обоснование

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

Лучшие практики

Используйте простой поток управления

  • Старайтесь использовать простой, единственный путь управления для каждой функции. Как правило, у вас должна быть одна точка возврата из функции.
  • Break и continue следует избегать в циклах, когда их можно легко избежать.
  • Gotos следует избегать, хотя они могут использоваться для очистки ошибок в некоторых случаях.
  • Сложные структуры управления часто можно избежать, используя больше и меньше функций.
  • Эти рекомендации следует применять с большой долей здравого смысла. Множественные возвраты, break, continue и goto могут использоваться, когда это уместно.

Утверждения

  • Используйте утверждения свободно.
  • Проверяйте параметры функции с помощью утверждений.
  • Тестируйте на распространённые ошибки, такие как разыменование NULL и деление на ноль, с утверждениями.
  • Утверждения предназначены для невозможных условий; они не заменяют обработку ошибок для законных условий отказа.

Обоснование

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

Вырезание и вставка

  • Если вы обнаруживаете, что копируете и вставляете большой фрагмент кода, спросите себя, не будет ли код лучше в подпрограмме, где все смогут его использовать.
  • Копируемый код имеет тенденцию быстро устаревать и способствует раздуванию кода.

Повторное использование локальных переменных

  • Не используйте локальную переменную для разных задач. Например, использование переменной с именем «temp» для трёх разных целей.
  • Это затрудняет чтение кода и, вопреки распространённому мнению, может фактически привести к большему и более медленному объектному коду.

Магические числа

  • Магические числа — это... Текст запроса определён как язык программирования C.

Перевод текста на русский язык:

Используйте #define или enums для присвоения символических имён константам вместо использования в коде литералов, кроме -1, 0, 1, 2.

Пример правильного использования:

enum {
    TOO_TALL = -4,
    MAX_HEIGHT = 7
}

if (height > MAX_HEIGHT) {
    return TOO_TALL;
}

Пример неправильного использования:

if (height > 7) {
    return -4;
}

Код для отладки

  • Пишите код с учётом отладки.
  • Оставляйте полезный код отладки (условный код, использующий #ifdefs).

Указатели функций

  • Часто структуры включают член-указатель функции. По возможности назовите указатель функции тем же именем, что и функция, на которую он будет указывать.

Пример правильного использования:

typedef struct OMRInterface {
    FUNCPTR doSomething;
} OMRInterface;

Пример неправильного использования:

typedef struct OMRInterface {
    FUNCPTR doSomethingPtr;
} OMRInterface;

Обоснование

Это упрощает поиск ссылок на функцию.

Математика

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

Пример правильного использования:

assert(newValue >= oldValue);
uint32_t delta = newValue - oldValue;

Пример неправильного использования:

uint32_t delta = newValue - oldValue;

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

Пример правильного использования:

assert(0 != oldValue);
double ratio = (double)newValue / (double)oldValue;

Пример неправильного использования:

double ratio = (double)newValue / (double)oldValue;

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

Пример правильного использования:

uint32_t count = getCount();
uint32_t size = getSize();
uint32_t averageSize = size / count;

Пример неправильного использования:

uint8_t count = getCount();
uint32_t size = getSize();
uint32_t averageSize = size / count;

Указание пространства имён в определении функции

При написании определения функции C++ отдельно от объявления используйте полностью квалифицированное имя, чтобы упростить поиск.

Неправильное использование:

namespace OMR { 

Foo::Func(int*) ... 

} 

Правильное использование:

OMR::Foo::Func(int*) 

Коды платформ

Использование макросов, специфичных для платформы, и определений компиляции — это тема стандартизации. Сегодня используются следующие макросы для проверки того, какая платформа является хостом для компиляции OMR в трёх измерениях.

Архитектура платформы

Макрос Архитектура
J9ARM ARM
PPC IBM Power Systems
OMRZTPF IBM z/TPF
TODO: код для s390 IBM System z
X86 x86
AMD64 x86-64

Битовая ёмкость

Макрос Битовая ёмкость
OMR_ENV_DATA64 64

Операционная система

В будущем мы хотели бы иметь флаги OMR_OS_xxx для операционной системы, аналогичные флагам OMR_ARCH_xxx.

Макрос Операционная система
AIX IBM AIX
LINUX GNU/Linux
OMR_OS_WINDOWS Microsoft Windows
OSX Apple OS X
J9ZOS390 IBM z/OS

Примеры

Правильное использование:

#if defined(LINUX) && !defined(OMRZTPF)

#if defined(OMR_OS_WINDOWS) && defined(OMR_ENV_DATA64)

#if defined( _AMD64_)

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-omr.git
git@api.gitlife.ru:oschina-mirror/mirrors-omr.git
oschina-mirror
mirrors-omr
mirrors-omr
master