На этой странице описывается руководство по стилю, которому следует UBLinux.
Каждый язык имеет свой уникальный способ выполнения задач. Большинство будет рассмотрено здесь.
Это руководство применимо к каждому новому фрагменту кода и каждому рефакторингу.
ПРИМЕЧАНИЕ: некоторые части системы довольно старые и не соответствуют этому руководству по стилю.
Со временем эти проблемы будут постепенно переходить к новому стилю.
Однако это не означает, что вам не нужно следовать руководству.
Это означает, что вы можете рефакторить этот фрагмент кода
Для начала мы хотели бы рассмотреть основные стили, применимые к большинству файлов.
ВСЕГДА делайте отступ кода/текста таким образом, чтобы его было легко прочитать.
Это означает, что каждая строка в одной и той же области видимости должна начинаться с одного и того же отступа.
Если у вас есть подобласть, отступ должен быть на один уровень глубже.
static void alert_destroy_all_active(void) {
size_t i;
for (i = 0; i < sizeof(active_notif) / sizeof(active_notif[0]); i++) {
if (active_notif[i]) {
NotifyNotification *n = active_notif[i];
active_notif[i] = NULL;
alert_destroy(n);
}
}
}
static void alert_destroy_all_active(void) {
size_t i;
for (i = 0; i < sizeof(active_notif) / sizeof(active_notif[0]); i++) {
if (active_notif[i]) {
NotifyNotification *n = active_notif[i];
active_notif[i] = NULL;
alert_destroy(n);
}
}
}
Открытие новой области всегда должно быть в предыдущей строке. Если язык не запрещает.
function validate {
# EXEC can be a command together with specific options
# This line retreives the command (the first parameter)
COMMAND="$(echo "$EXEC" | cut -d" " -f1 )"
if [[ ! "$(command -v $COMMAND)" ]]; then
log "$LOG_ERROR" "Command $COMMAND is not installed."
exit 1
fi
}
function validate
{
# EXEC can be a command together with specific options
# This line retreives the command (the first parameter)
COMMAND="$(echo "$EXEC" | cut -d" " -f1 )"
if [[ ! "$(command -v $COMMAND)" ]]; then
log "$LOG_ERROR" "Command $COMMAND is not installed."
exit 1
fi
}
Предпочтение удобочитаемости нежели эффективности/размера
function Change_Metric {
current=$(ip route | grep -E "^default .* $1")
new=$(printf "%s" "$current" | sed "s/metric [0-9]*/metric /")"$2"
ip route del "$current"
ip route add "$new"
}
function Change_Metric {
ip route del "$(ip route | grep -E ^default .* $1)"
ip route add $(printf "%s" "$(ip route | grep -E ^default .* $1)" | sed "s/metric [0-9]*/metric /")"$2"
}
Сценарии оболочки должны быть помечены bash как исполняемые, чтобы их можно было вызывать без самостоятельного вызова интерпретатора. Исполняемый файл всегда должен быть bash, а не zsh или другой оболочкой.
#!/bin/bash
echo "Hello, World!"
echo "Hello, World!"
#!/bin/sh
echo "Hello, World!"
Все ошибки должны быть перенаправлены в stderr. Это означает, что не записывать сообщения об ошибках в стандартный вывод.
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
}
if ! do_something; then
err "Unable to do_something"
exit 1
fi
err() {
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*"
}
if ! do_something; then
err "Unable to do_something"
exit 1
fi
Всегда проверять ошибки внутри скрипта и выходить при необходимости
makepkg -s --sing --key "$KEY" PKGBUILD || exit 1
makepkg -s --sing --key "$KEY" PKGBUILD
Небольшой, но лаконичный комментарий в верхней части каждого скрипта должен присутствовать, чтобы люди знали, что делает скрипт.
# Этот скрипт должен быть обратно совместим
# Всегда проверяйте, существует ли файл/каталог
# Не забывайте, что более ранняя установка может использовать файл в других местах, где ожидалось
# Например, миграция из пользовательского каталога .config в общесистемный каталог /etc/xdg
# Это потребовало удаления каталога .config/ublinux. Это мог быть пользователь, который специально написал скрипты в этом каталоге.
# Другими словами, если что-то удаляете, запросите разрешение пользователя
# Этот скрипт выполняет обновление
при использовании цикла условия поставьте ; then или; do в той же строке, что и условие/цикл
for i in *.sh; do
echo "$i"
done
for i in *.sh
do
echo "$i"
done
Всегда заключайте переменные в кавычки, чтобы предотвратить расширение.
echo "$VAR"
echo $VAR
Выполнить проверку документа. Используйте флаги исключения вверху, чтобы отключить проверки, которые вам не нужны.
Используйте команду [[ вместо [
# Это гарантирует, что строка слева состоит из символов в
# класс символов "alnum", за которым следует имя строки.
# Обратите внимание, что правая сторона здесь не должна заключаться в кавычки.
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
# Это точно соответствует шаблону "f*" (в данном случае не совпадает)
if [[ "filename" == "f*" ]]; then
echo "Match"
fi
# Это дает ошибку "слишком много аргументов", так как f* расширяется до
# содержимое текущего каталога
if [ "filename" == f* ]; then
echo "Match"
fi
Объявляйте функции с помощью ключевого слова function
function check() {
echo "Checking..."
}
check {
echo "Checking..."
}
Постоянные переменные, а также переменные окружения должны быть написаны заглавными буквами.
XDG_CONFIG_DIR="blabla"
xdg_config_dir="blabla"
Используйте полные слова, за исключением редких случаев, когда аббревиатура будет более каноничной и более понятной.
size_t character_size;
size_t length;
short tab_index; // Более канонично.
size_t char_size;
size_t len;
short tabulation_index; // Глупо.
Элементы данных в классах должны быть закрытыми. Статические элементы данных должны иметь префикс s\_. Другие элементы данных должны иметь префикс m\_. Глобальные переменные должны иметь префикс g\_.
class String {
public:
...
private:
int m_length { 0 };
};
class String {
public:
...
int length { 0 };
};
Перед сеттерами ставится слово set. Используйте голые слова для геттеров. Имена сеттера и геттера должны совпадать с именами устанавливаемых/получаемых переменных.
void set_count(int); // Sets m_count.
int count() const; // Returns m_count.
void set_count(int); // Sets m_the_count.
int get_count() const; // Returns m_the_count.
Перед геттерами, возвращающими значения через аргументы out, стоит слово get.
void get_filename_and_inode_id(String&, InodeIdentifier&) const;
void filename_and_inode_id(String&, InodeIdentifier&) const;
Используйте описательные глаголы в именах функций.
bool convert_to_ascii(short*, size_t);
bool to_ascii(short*, size_t);
Уберите бессмысленные имена переменных из объявлений функций. Хорошее эмпирическое правило заключается в том, что если имя типа параметра содержит имя параметра (без конечных чисел или множественного числа), то имя параметра не требуется. Обычно должно быть имя параметра для логических, строковых и числовых типов.
void set_count(int);
void do_something(Context*);
void set_count(int count);
void do_something(Context* context);
Предпочитайте перечисления логическим параметрам функций, если вызывающие объекты могут передавать константы, поскольку именованные константы легче читать на месте вызова. Исключением из этого правила является функция установки, где имя функции уже ясно дает понять, что такое логическое значение.
do_something(something, AllowFooBar::Yes);
paint_text_with_shadows(context, ..., text_stroke_width > 0, is_horizontal());
set_resizable(false);
do_something(something, false);
set_resizable(NotResizable);
Члены перечисления должны использовать InterCaps с начальной заглавной буквы.
Предпочитайте const вместо #define. Предпочитайте встроенные функции макросам.
#defined константы должны использовать все имена в верхнем регистре со словами, разделенными символами подчеркивания.
Используйте #pragma Once вместо #define и #ifdef для защиты заголовков.
// MyClass.h
#pragma once
// MyClass.h
#ifndef MyClass_h
#define MyClass_h
Конструкторы для классов C++ должны инициализировать свои члены, используя синтаксис инициализатора C++. Каждый элемент (и суперкласс) должен располагаться с отступом на отдельной строке с двоеточием или запятой перед членом в этой строке. По возможности отдавайте предпочтение инициализации при определении члена.
class MyClass {
...
Document* m_document { nullptr };
int m_my_member { 0 };
};
MyClass::MyClass(Document* document)
: MySuperClass()
, m_document(document)
{
}
MyOtherClass::MyOtherClass()
: MySuperClass()
{
}
MyClass::MyClass(Document* document) : MySuperClass()
{
m_myMember = 0;
m_document = document;
}
MyClass::MyClass(Document* document) : MySuperClass()
: m_my_member(0) // This should be in the header.
{
m_document = document;
}
MyOtherClass::MyOtherClass() : MySuperClass() {}
Предпочитайте index или range-for итераторам в итерациях Vector для краткого, легко читаемого кода.
for (auto& child : children)
child->do_child_thing();
for (int i = 0; i < children.size(); ++i)
children[i]->do_child_thing();
for (auto it = children.begin(); it != children.end(); ++it)
(*it)->do_child_thing();
Указатели и ссылочные типы в коде C++
И указатели, и ссылочные типы должны быть записаны без пробела между именем типа и * или &.
Аргумент out функции должен передаваться по ссылке, за исключением редких случаев, когда он необязателен, и в этом случае он должен передаваться по указателю.
void MyClass::get_some_value(OutArgumentType& out_argument) const
{
out_argument = m_value;
}
void MyClass::do_something(OutArgumentType* out_argument) const
{
do_the_thing();
if (out_argument)
*out_argument = m_value;
}
void MyClass::get_some_value(OutArgumentType* outArgument) const
{
*out_argument = m_value;
}
В файлах реализации C++ не используйте объявления using любого типа для импорта имен в стандартной библиотеке шаблонов. Непосредственно уточняйте имена в том месте, где они используются вместо этого.
// File.cpp
std::swap(a, b);
c = std::numeric_limits<int>::max()
// File.cpp
using std::swap;
swap(a, b);
// File.cpp
using namespace std;
swap(a, b);
Опускать int при использовании модификатора unsigned. Не используйте модификатор signed. Вместо этого используйте int сам по себе.
unsigned a;
int b;
unsigned int a; // Не пропускает "int".
signed b; // Использует "signed" вместо "int".
signed int c; // Не пропускает "signed".
Для типов с методами предпочтительнее class, чем struct.
m_.m_.struct Thingy {
String name;
int frob_count { 0 };
};
class Doohickey {
public:
const String& name() const { return m_name; }
int frob_count() const { return m_frob_count; }
void jam();
private;
String m_name;
int m_frob_count { 0 };
}
struct Thingy {
public:
String m_name;
int frob_count() const { return m_frob_count; }
private:
int m_frob_count { 0 };
}
class Doohickey {
public:
const String& name() const { return this->name; }
void jam();
String name;
int frob_count { 0 };
};
Используйте конструктор для выполнения неявного преобразования, когда аргумент разумно рассматривается как преобразование типа, а преобразование типа происходит быстро. В противном случае используйте явное ключевое слово или функцию, возвращающую тип. Это относится только к конструкторам с одним аргументом.
class LargeInt {
public:
LargeInt(int);
...
class Vector {
public:
explicit Vector(int size); // Не преобразование типа.
Vector create(Array); // Дорогостоящая конверсия.
...
class Task {
public:
Task(ExecutionContext&); // Не преобразование типа.
explicit Task(); // Без аргументов.
explicit Task(ExecutionContext&, Other); // Более одного аргумента.
...
Используйте статическую функцию-член с именем the() для доступа к экземпляру синглтона.
class UniqueObject {
public:
static UniqueObject& the();
...
class UniqueObject {
public:
static UniqueObject& shared();
...
class UniqueObject {
...
};
UniqueObject& my_unique_object(); // Бесплатная функция.
Сделайте так, чтобы комментарии выглядели как предложения, начиная с заглавной буквы и заканчивая точкой (пунктуация). Единственным исключением могут быть комментарии в конце строки, такие как if (x == y) // false для NaN.
Используйте FIXME: (без указания авторства) для обозначения элементов, которые необходимо решить в будущем.
draw_jpg(); // FIXME: Сделайте так, чтобы этот код обрабатывал jpg в дополнение к поддержке png.
draw_jpg(); // FIXME(joe): Сделайте так, чтобы этот код обрабатывал jpg в дополнение к поддержке png.
draw_jpg(); // TODO: Сделайте так, чтобы этот код обрабатывал jpg в дополнение к поддержке png.
Объявление виртуального метода внутри класса должно быть объявлено с ключевым словом virtual. Все подклассы этого класса должны либо указывать ключевое слово override при переопределении виртуального метода, либо ключевое слово final при переопределении виртуального метода и требовать, чтобы никакие другие подклассы не могли его переопределить.
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
virtual String description() override { ... }; // Это правильно, потому что содержит только ключевое слово "override", указывающее, что метод переопределен.
}
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
virtual String description() final { ... }; // Это правильно, потому что содержит только ключевое слово "final", указывающее, что метод переопределен и что никакие подклассы "Student" не могут переопределить "description".
}
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
String description() override { ... }; // Это неверно, потому что для обозначения того, что метод является виртуальным, используются только ключевые слова "override". Вместо этого следует использовать ключевые слова «virtual» и «override».
}
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
String description() final { ... }; // Это неверно, потому что используются только ключевые слова "final", чтобы указать, что метод является виртуальным и окончательным. Вместо этого следует использовать как «virtual», так и «final» ключевые слова.
}
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
virtual String description() { ... }; // Это неверно, поскольку используется ключевое слово «virtual», чтобы указать, что метод переопределен.
}