Russian Qt Forum

Разное => Говорилка => Тема начата: Azazello от Сентябрь 21, 2019, 23:17



Название: Style guide
Отправлено: Azazello от Сентябрь 21, 2019, 23:17
Видел очень, очень много кода, которые преподаватели дают студентам для написания программ в ведущих университетах мира - от США до Японии (не шучу).
Кстати, что интересно, во многих случаях к каждому заданию прикреплен файлик про стиль написания и даже что в кампусе приставать ни кому нельзя.

Это к чему: знаю про что говорю.

Мне все равно, с каким Style guide работать, перехожу на него свободно, но порассуждаю, на то она и говорилка. (Вру, не совсем все равно, но частый переход между разными исходниками делают тебя менее чувствительным). Может показаться диким - но смотря какие приоритеты: вальяжно лазить по коду или у тебя времени несколько часов для решения задачи.

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

Вопрос про префикс get: чтобы уменьшить объем рассуждений. name() или get_name() (getName) пропускаю.
Первый вариант красивее, второй однозначнее.

Вообще, не зависимо от языка программирования, все знают, первая сложность - это дать имена.

И так, стиль Qt

Код:
class Object 
{
public:
   string name()
   void setName(...)
private:
   string m_name;
}

Сразу невосприятие: С какого перепугу для внутреннего представления идет разделение по подчеркиванию, для внешнего - по заглавным буквам.
Если уж так, называйте тогда mName.

Код:
class Object 
{
public:
   string name( return this->name;)
   void setName(...)
private:
   string name;
}

Тут вообще путница полная (обратить внимание на this). Я называю (для себя) стилем PHP.
Очень легко ошибиться. Но даже не в ошибке дело. unit тесты все исправят.
Вынос мозга, при написании кода, когда name может быть стековый или принадлежать классу. Задержки не большие, но если в функции много таких переменных зависание обеспечено.

Код:
class Object 
{
public:
   string name( return _name;)
   void set_name(...)
private:
   string _name;
}

Отлично -  приемлемый вариант.

Код:
class Object 
{
public:
   string name( return m_name;)
   void set_name(...)
private:
   string m_name;
}

Зачем лишний m (m_name)? См. выше.

Область видимости:

Код:
class Object 
{
   string m_name;
public:
   string name( return m_name;)
   void set_name(...)
}

Тут самое сложное. Многие этим грешат. А что не так?

Для меня не так следующее: Важность данных должна быть сверху вниз. Приватные данные менее важны чем публичные.

Для меня класс выглядит так:

Код:
class Object 
{
  //приватные данные, которые должны бросаться в глаза
   friend class .....   -
   using type = sometype
public:
  using type = sometype //публичный тип

   string name( return m_name;)
   void setName(...)

private:
    приватные функции

private:
    приватные переменные.
}

Блоки: тут сложно.
Скобки с новой строки, восприятие однозначное

Код:
void f()
{

}

Сколько раз пробывал:

Код:
void f() {

}

не идет. Аргументировать не могу.

Код:
if (val)
    k = b;

Лаконично, но играет злую шутку, если вы иногда используете другой вариант

Код:
if (val) {
    k = b;
}

В чем загвоздка - если из одного стиля вы переходите на другой, при дебаге вы можете сделать так:
Код:
if (val) {
    //k = b;
}
Вот отгребаешь, когда автоматом закоментил......
Код:
if (val)
   // k = b;

Ошибки == и = (перечитались Маерса)
Типа должно быть:
Код:
enum E {
    V1,
    V2,
    ....
}

if (V1 == value) {
}

В чем смысл, V1 - константа. Мы не можем ошибиться с "==" и "=", т.к. с левой стороны константа.

Это в теории. Если вы ошиблись, беда вам без статического анализатора и/или юнит тестов.
Но такой код не воспринимается.

На практике, такой код гораздо более читаем:
Код:
if (value == V1) {
}

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

Усложнение функций с помощью if

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

Код:
int someFunction (p1, p2 .....) 
{
     if (p1 == nullptr) {
        return -1;
     } else {
        SomeCode
     }
}

Что не так?
Другой вариант.

Код:
int someFunction (p1, p2 .....) 
{
     if (p1 == nullptr)
        return -1;

     SomeCode

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

Код:
int someFunction (p1, p2 .....) 
{
     if (p1 == nullptr)
        return -1;

     if (size() > 0 && val != 0)
       return -1;

    SomeCode;

В общем случае, вывод пограничных проверок вверх не только увеличивает читаемость кода, но и иногда (да ладно, 80%) увеличивает производительность.
Может показаться смешным, но увеличение производительности связано с простой вещью: вы избавляете себя от лишних условий для кода в дальнейшем. А самые умные из нас не больше 6-7 объектов могут хранить :)

auto

Использование по Маерсу приводит к проблемам.
Общий случай - только для шаблонов.

Что значит для шаблонов?
auto it = std::find

При использовании auto i = size() приводит к полному невосприятию кода.
Если мы задницей понимаем, что i - это разновидность int, то более сложные случаи ужасны.

Qt не совместим с std по интам. Что мы используем std::vector или QList?
static_cast в коде ужасен повсеместно, но покрайней меры мы должны видеть на уровене кода, что мы получаем
std::size_t или int

И, други мои, это не руководство к действию, это мои никому не нужные размышления.


Название: Re: Style guide
Отправлено: _Bers от Сентябрь 22, 2019, 11:31
это мои никому не нужные размышления.

действительно - не нужные.



Название: Re: Style guide
Отправлено: ViTech от Сентябрь 22, 2019, 11:45
Это к чему: знаю про что говорю.

Странно, что в теме про code style, этот самый code не оформлен тегами code.

Всё это субъективно и дело вкуса. Каждому подходу можно своё обоснование привести. Кому-то это будет важно, другим - нет. Потому такое разнообразие стилей и наблюдается.


Название: Re: Style guide
Отправлено: Azazello от Сентябрь 23, 2019, 15:29
Это к чему: знаю про что говорю.

Странно, что в теме про code style, этот самый code не оформлен тегами code.

Всё это субъективно и дело вкуса. Каждому подходу можно своё обоснование привести. Кому-то это будет важно, другим - нет. Потому такое разнообразие стилей и наблюдается.

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

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


Название: Re: Style guide
Отправлено: Azazello от Сентябрь 23, 2019, 15:31
это мои никому не нужные размышления.

действительно - не нужные.



Не, ну прошёл бы мимо. Ладно был бы топик - делай как я или это была бы не говорилка.


Название: Re: Style guide
Отправлено: Igors от Октябрь 01, 2019, 08:02
Ну стиль есть стиль, как говорится "на вкус и цвет..", поэтому эту говорильню я не поддержу  :) Но идея разговоров на довольно общие темы мне нравится, не все должно сводиться к "конкретной кнопке" :) Предложу подобную тему

Вот эти вот public/private... Конечно общепринятые рекомендации "в общем справедливы", поэтому многие начинают класс с рисования геттеров/сеттеров (ГС) в хедере. Я частенько стартую без всяких protected/private, и только потом, когда почувствую "что-то не так" начинаю засисяться. Полученные таким образом ГС получаются гораздо лучше, содержательней.
Цитировать
Так может лучше сразу было сделать "правельно"?
Не выходит :) Пока класс не связан с другими, ГС лепятся довольно механично, в итоге все равно получается public только прикрытый "фиговым листочком". Да, и вообще ГС нужны далеко не всегда. Нередко с public все прекрасно и никакой защиты не требует. Аргументы типа "не отстрелить ногу" явно слабы, с ГС отстрелить ее легче, ведь формально все правильно, что придает уверенности  :)


Название: Re: Style guide
Отправлено: Azazello от Октябрь 01, 2019, 18:22
Ну стиль есть стиль, как говорится "на вкус и цвет..", поэтому эту говорильню я не поддержу  :) Но идея разговоров на довольно общие темы мне нравится, не все должно сводиться к "конкретной кнопке" :) Предложу подобную тему

Вот эти вот public/private... Конечно общепринятые рекомендации "в общем справедливы", поэтому многие начинают класс с рисования геттеров/сеттеров (ГС) в хедере. Я частенько стартую без всяких protected/private, и только потом, когда почувствую "что-то не так" начинаю засисяться. Полученные таким образом ГС получаются гораздо лучше, содержательней.
Цитировать
Так может лучше сразу было сделать "правельно"?
Не выходит :) Пока класс не связан с другими, ГС лепятся довольно механично, в итоге все равно получается public только прикрытый "фиговым листочком". Да, и вообще ГС нужны далеко не всегда. Нередко с public все прекрасно и никакой защиты не требует. Аргументы типа "не отстрелить ногу" явно слабы, с ГС отстрелить ее легче, ведь формально все правильно, что придает уверенности  :)


Для меня это не так. Просто именно для меня. Раньше это было по другому.
Если мы берём std, там все стройно (в плане интерфейса), но мы пишем реальные программы, где бардак ещё тот, как бы мы не старались.

И паблик переменная, в классе, меня психологически смущает (ну разве что кроме static constexpr npos = -1)
Когда вижу структуру, у меня включается, что это не класс и там паблик самое то.

Что же меня смущает - если переменная изменилась, и это не повлияло на класс(объект), зачем же она нужна?.
Да, тысячи случаев, зачем она нужна без сеттеров, но....

Ну, и кроме этого, когда мы используем ГС, мы осознаем, что происходит копирование/перемещение. Мы можем поставить noexcept, который даст нам дополнительную информацию и мы точно знаем, что происходит. Это не имеет значение, если переменная никогда не будет закрыта, но Вы говорите о - "когда надо, тогда и запулячу её туды".

А использование такой примитивной конструкции для дебага/кеширования?
auto name = this->name();
При перевод паблика в приват иногда может потребоваться много усилий приложить.

Если говорить по другому - это сводится к правилам, которые я не хочу нарушать, чтобы вообще по этому поводу мозги не включать. Понятное дело, что например для написания юнит тестов никаких лишних мыслей нет и там все тупо и прозрачно до безобразия (для классов обслуживающих эти тесты) и там я отрываюсь, наплевав на все правила. Ну или pimpl, где это по умолчанию, потому что "защиту" вам обеспечивает надстройка.


Название: Re: Style guide
Отправлено: Igors от Октябрь 03, 2019, 08:47
Что же меня смущает - если переменная изменилась, и это не повлияло на класс(объект), зачем же она нужна?.
Ну вот хотя бы сейчас этим занимаюсь
Код
C++ (Qt)
struct CEmitter {
...
std::vector<CEmitInst> mInstances;
...
};
Есть по меньшей мере еще 2 класса которые будут использовать этот вектор: "генератор" (отвечающий за наполнение вектора), и "диалог" (юзер также может всяко менять). Ну и конечно сам владелец немало с ним делает.

И вот пока я не вижу никаких (ну совсем никаких) оснований этот вектор защищать. Написать пяток ГС нетрудно, но это носит чисто "декоративный" или "ритуальный" характер :) Ну в самом деле, чего мы хотим? Если доступ по записи нужен др классам, так он и нужен. Да, почти каждая запись влечет какие-то доп действия, обвязки. Напр если "диалог" удалил элемент(ы) вектора, он должен перерисовать UI. Но очевидно такие действия надо помещать в "диалог"  (т.е. в "пишущего", а не во "владельца").

Может тогда неверно хранить вектор в классе CEmitter? А тут особого выбора нет. Ни "генератор", ни "диалог" очевидно для хранения не годятся. А вектор по меньшей мере должен быть (де)сериализован, ну и вообще "существовать" :)


Название: Re: Style guide
Отправлено: Azazello от Октябрь 03, 2019, 15:07
Напр если "диалог" удалил элемент(ы) вектора, он должен перерисовать UI. Но очевидно такие действия надо помещать в "диалог"  (т.е. в "пишущего", а не во "владельца").

Для меня здесь не все так очевидно. Это мне нужно знать, что диалог блокирующий, да ещё и других "с таким же функционалом" нет. И если всё забыть, и вернуться к исходникам заново, мне долго и нудно нужно искать, где происходит "перерисовка". Гораздо проще её всё таки найти в гетере/сеттере возле источника данных (в данном случае это обертка конечно) перерисовку, либо рассылку сообщений.

Другими словами:  я с вами не согласен, что нужно помещать "перерисовку" в "пишущего", а не во владельца (либо рассылку сообщений). Для меня это работает, когда весь проект помещается в голове и он однозначно ВСЕГДА будет помещаться в голове. А-ля autorun для диска. Сколько не малодушничал в этом плане, всегда возвращалось бумерангом.

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

Анекдот:
Приходит проверяющий в бар, заказывает 50 граммов водки.
Бармен наливает, проверяющий тут же переливает её в измерительный стаканчик - 40 граммов! Бармена штрафуют.
Через неделю тот же проверяющий в том же баре у того же бармена снова заказывает 50 граммов водки. Переливает в измерительный стаканчик - снова 40 граммов! Снова штрафует.
Ещё через неделю история повторяется. Проверяющий не выдерживает:
- Я к вам уже в третий раз прихожу, вы меня уже в лицо знаете. Я вас каждый раз штрафую, но вы всё равно упорно наливаете 40 граммов!
Бармен:
- Да мне проще вам штраф заплатить, чем руку сбить!

P.S.
CEmitter
А что значит C? Это с тех бархатных времён?

P.P.S.
И почему эмиттер, если он эмит не делает?


Название: Re: Style guide
Отправлено: Igors от Октябрь 04, 2019, 07:08
CEmitter
А что значит C? Это с тех бархатных времён?
Да, по той же причине что "Q" в Qt
И почему эмиттер, если он эмит не делает?
Делает, я упоминал о классе "генератор", но созданное надо хранить и редактировать

Другими словами:  я с вами не согласен, что нужно помещать "перерисовку" в "пишущего", а не во владельца (либо рассылку сообщений). Для меня это работает, когда весь проект помещается в голове и он однозначно ВСЕГДА будет помещаться в голове. А-ля autorun для диска. Сколько не малодушничал в этом плане, всегда возвращалось бумерангом.
Ну во-первых как Вы это себе представляете для контейнера?
Код
C++ (Qt)
void CEmitter::SetInstanceID( size_t instIndex, TUnique id )
{
 mInstance[instIndex].mID = id;
}
Придется рисовать такое для каждого члена CEmitInst (эл-та контейнера). И чего мы добились? Что защитили? Переливания "из пустого в порожнее"

А так
Код
C++ (Qt)
void CEmitter::SetInstanceID( size_t instIndex, TUnique id )
{
 mInstance[instIndex].mID = id;
 emit instanceChanged(id);
}
Неоднократно убеждался что такая популярная конструкция часто плоха. Напр мы проходим по всем эл-там контейнера - и на каждом эмиттим сигнал, как минимум неаккуратно. А главное - "контекст" для получателя сигнала утерян. Ряд Qt-шных "штатных" сигналов этим страдает. Типа selectionChanged - ах как было бы здорово если б знать что это именно "ввод юзера", а так дешевле и лучше обойтись без него.

Для меня это работает, когда весь проект помещается в голове и он однозначно ВСЕГДА будет помещаться в голове. А-ля autorun для диска. Сколько не малодушничал в этом плане, всегда возвращалось бумерангом.
Не понял в чем проблема - ну кому надо - тот и апдейтит, зачем это помнить? А вот помещать в класс всякие посторонки, хоть и замазанные сигналами, не есть хорошо