Обновить
4

Пользователь

0,1
Рейтинг
Отправить сообщение

Это уже не статья, это почти полноценная справка. Мне кажется, если контент статьи как-то удачнее сгруппировать или расставить теги над каждым топиком, большая часть этих топиков была бы уместна даже на cppreference в каком-нибудь самостоятельном разделе

Вы ошиблись с картинкой, исправляю

C++ in 21 day
alt
C++ in 21 day by time-travel meme

Завтра кто-то добавил акроним: _URL

Писать акроним капсом – это всё ещё плохая идея безотносительно подчёркивания, поскольку имя переменной капсом пересекается с общепринятым неймингом констант или макросов

И возможно, что на практике такой нейминг в процессе ревью нанесёт даже больший ущерб, чем ifndr из-за подчёркивания

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

Что насчёт трудоёмкости поиска этих ошибок?

А можно прямо ссылку на пункт стандарта, где такое с int-типами противопоказано?

Это противопоказано в общем случае в basic.lval для любых типов (кроме небольшого списка исключений в начале абзаца, в который пара из int32_t и int64_t не входит):

If a program attempts to access the stored value of an object through a glvalue through which it is not type-accessible, the behavior is undefined

Для нетривиальных желаний есть std::bit_cast и std::start_lifetime_as. Однако первый потребует копирования, а в допустимости применения второго в данном случае я вообще не уверен

ИИ не понимает, что int64_t это будет дважды по int32_t. Для него это несвязанные слова и типы

И хотя мы достоверно не знаем, прав здесь ИИ или нет (это может зависеть от фактических типов аргументов), чушь нагаллюцинировал тут, всё же, не он, а вы

Да, такое понятие как strict aliasing действительно существует и оно действительно не позволяет доступ к int64_t через указатель на объект типа int32_t (при условии, что по указателю действительно лежит объект типа int32_t). Даже если их там два подряд лежит. Да, для стандарта языка и компилятора это тоже “несвязанные слова и типы”

Просто потому что стандарт языка оперирует объектной моделью. Всё в языке является объектом некоторого типа. И чтобы по указателю достать какое-то значение, нужно туда сначала положить объект или этого же типа, или типа, через который int64_t будет type-accessible. В нашем случае под указателем на int32_t практически наверняка никто заранее не создавал объект типа int64_t или объект, через который он type-accessible. Потому доступ к такому несуществующему объекту попросту нелегален с точки зрения языка, чтобы там где не лежало

Это значит, что компилятор будет при оптимизации исходить из предположения, что так делать нельзя. И если вы, всё же, сделали то, чего компилятор от вас не ожидал… Что он накомпилирует, исходя из ложного предположения, никто не знает. Стандарт говорит, что что-то неопределённое

Великолепно. Вы только что ограничили возможности std::lock_guard, std::scoped_lock и других RAII-механизмов

Какие последствия вы выбираете вместе с этим "решением"?

  1. Падение производительности: замену маленьких скоупов критических секций, которые можно было выразить раньше на те большие скоупы, что можно с вашим предложением?

  2. Снижение надёжности и безопасности кода: из-за перехода на хрупкие ручные lock/unlock, которые можно забыть вызвать из-за невнимательности или необработанного внезапного исключения?

  3. Предпочтёте генерировать под это дело уродливые вымученные конструкции вроде такой?

do {
  std::scoped_lock lock(mutex);
  // critical section goes here...
} while(false);

Я отвечу за вас

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

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

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

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

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

Возможно, это объясняется тем, что trailing comma — удобная и сверхпопулярная вещь, а вот leading comma практически не встречается

Вообще, по секрету, в баше есть ещё один вариант (или, скорее, их семейство):

x="--flag-one"
y="--flag=value with spaces"
script -c "gcc ${x@Q} ${y@Q}"

И иногда это единственный доступный вариант, если утилита хочет шелл-команду одной цельной строчкой. Как script, например

Прямо сейчас у меня в initramfs нет баша, вообще. И очень вряд ли, что у вас иначе. А шелл-скрипты там есть и бывают в немалом количестве. У меня там ash, вроде

А ещё в дебиане вызовется dash, если ваш шебанг вдруг окажется так весьма популярным #!/bin/sh вместо #!/bin/bash

А ещё нередко встречается busybox в эмбеде, там тоже ash

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

А иногда даже бывает нужна совместимость с zsh, в котором есть массивы, но доступ в которых работает иначе. Это, например, важно для скриптов автокомплита. Там вариант с bash -c несколько затруднителен

а там где не bash - всегда можно вызвать bash - c

Это не всегда решает автор скрипта

Стена текста - это проблема того, кто код пишет

В первую очередь – того, кто читает код. Потому что чем больше стена, тем труднее верно оценить её (во всех смыслах). Страдает не только процесс разработки, но и ревью, и аудита. Да даже банально будут страдать коллеги, которые работают с тобой в общей кодовой базе

Речь про этот пример из статьи:

if (condition)
  do1();
  do2();

Анализатор прекрасно видит несоответствие отступов отсутствию фигурных скобок. Он это безошибочно подчеркнёт как misleading indentation

А первый пример банально нереалистичен и рассматривать его нет никакого смысла. Потому что стейтменты обычно длинные и в одну строчку, да ещё и с ифом обычно не влезают – не пройдут ревью

Я вот лично попадал в другую нелепую ситуацию:

if (foo) {

} else if (bar) {

} else if (baz) {

} { // потерял else

}

Что предложите ещё запретить, чтобы решить ещё и эту проблему? Или, может, всё же, запреты это не панацея?

Ещё есть пакеты Flatpak, которые изолируют приложение, но вообще-то могут быть созданы кем угодно

Так и deb-пакеты может кто угодно. Суть не в сборке, а в репозитории и мейнтейнерах. Не доверяете Flathub? У RedHat есть свой репозиторий flatpak-пакетов от их мейнтейнеров

Или Docker, который изолирует приложение, но поди запусти его с GUI

Да, прокинуть переменную для дисплея и сокет от xorg/wayland – это неподъемная задача, конечно. Никто не справится:

docker run -it                                         \
  -e XDG_RUNTIME_DIR=/tmp                                \
  -e WAYLAND_DISPLAY                                       \
  -v $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY:/tmp/$WAYLAND_DISPLAY \
   container app

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

а права на файлы я всё так же выставляю циферками 0+1+2+4

Вас никто не заставляет так делать. Современный chmod вполне принимает опции в формате chmod u=rwx,g-w,o+x

Сделайте фигурную скобку { обязательной после if — никто почти и не заметит, но эта ошибка умрёт навсегда

А ничего, что статические анализаторы в современном мире и так подчёркивают жёлтым ошибочные конструкции? Нет, действительно, давайте уж вместо этого писать такие полотна, убивающие early return:

if (not valid(foo))
{
    return error("don't do this");
}

Ну а кому не будет приятно увидеть такой шедевр искусства в своей кодовой базе?

Дополнительно в C++ добавили... Нет, не так. В C++ СПЕЦИАЛЬНО добавили случаи, при которых происходит хрен знает что. Не программа падает, нет. ХРЕН ЗНАЕТ ЧТО происходит. Undefined behavior. Собственно, на что был расчёт?

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

А ведь если бы SQL разделял запрос и данные (SELECT %s FROM %s, (name, age), users) и делал бы подстановку именно значений, а не текста, то не было бы инъекций

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

Это запишет в переменную окружения KEY пустое значение: environment: { KEY: }
Это передаст значение KEY с хоста внутрь контейнера: environment: [ KEY ]
Во-первых, почему можно написать одно и то же разными способами? Во-вторых, почему работает по-разному?!

Во-первых, обосную значимость второго. Иметь возможность форвардить переменную, не задавая и не вычисляя её значения – это очень полезно и наглядно. Намного нагляднее, чем environment: [ "KEY=${KEY}" ]

Во-вторых, первое работает по той причине, что yaml – это отдельная от docker compose сущность. Его парсер, вероятно, не позволяет отличить environment: { KEY: "" } от environment: { KEY: }. И потому эти две записи не могут давать разный результат. Либо обе дают пустую строку, либо обе форвардят значение

А вы бы хотели, чтобы environment: { KEY: "" } передавал не KEY, равный пустой строке, а форвардил непустое значение? Я бы этого не хотел, это было бы контринтуитивно

Я уж молчу о том, что даже вне контекста достаточно здравого смысла, чтобы понять, что KEY: задаёт <ничего>. И пустая строка является одним из ожидаемых результатов для <ничего>. И точно так же интуитивно очевидно, что в KEY нет задания значения, а потому это особый случай, который тоже интуитивно понятен большинству

Именованные аргументы всегда лучше, потому что они явные и не ломаются при рефакторинге

Это не так. Иногда у аргументов бывает натуральный порядок. Ну, например, вот: divide(numerator = 42, denominator = 123). Или join(first = root, second = subdirectory, ??? = filename). Кстати, да, first, second,... А до скольки нам так имена резервировать?

Кроме того, иногда аргументы выводимы из контекста, а перепутать их нельзя

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

А у стен текста есть объективная проблема. Их труднее парсить глазами, труднее даётся визуальная навигация по коду. Это всё приводит к более быстрому истощению внимания разработчика. Быстрее истощается внимание – больше ошибок за 8 рабочих часов. Либо просто ежедневно работаем меньше – пишем меньше фич

Информация

В рейтинге
3 617-й
Зарегистрирован
Активность