Это уже не статья, это почти полноценная справка. Мне кажется, если контент статьи как-то удачнее сгруппировать или расставить теги над каждым топиком, большая часть этих топиков была бы уместна даже на cppreference в каком-нибудь самостоятельном разделе
Писать акроним капсом – это всё ещё плохая идея безотносительно подчёркивания, поскольку имя переменной капсом пересекается с общепринятым неймингом констант или макросов
И возможно, что на практике такой нейминг в процессе ревью нанесёт даже больший ущерб, чем 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-механизмов
Какие последствия вы выбираете вместе с этим "решением"?
Падение производительности: замену маленьких скоупов критических секций, которые можно было выразить раньше на те большие скоупы, что можно с вашим предложением?
Снижение надёжности и безопасности кода: из-за перехода на хрупкие ручные lock/unlock, которые можно забыть вызвать из-за невнимательности или необработанного внезапного исключения?
Предпочтёте генерировать под это дело уродливые вымученные конструкции вроде такой?
Вы своим "гениальным" решением выбрали одновременно все варианты. Все плохие варианты. Потому что кто-то в вашей кодовой базе в одном месте предпочтёт потерю производительности, в другом кто-то предпочтёт хрупкий небезопасный код, а где-то ещё кто-нибудь предпочтёт обойтись просто уродливой конструкцией, вызывающей загадки на ревью и мистические слухи среди стажёров
Поздравляю, вы только что сделали код на несколько порядков более хрупким, багованным и сложноподдерживаемым... Там, где его и так было сложно отлаживать, где анализаторы практически не помогают — в критических секциях
И всё ради того, чтобы... Что? Чтобы убрать необходимость проверки от... анализатора, который у вас и так должен быть настроен в проекте по тысяче других веских причин? От линтера в IDE, который у вас и так наверняка есть из коробки? Или от специального флага компиляции компиляции, который тривиально добавить в сборку?
Так вот, иногда маленькие необдуманные дизайн-решения и запреты выливаются в огромные баги на ровном месте. Иногда, если совсем не думать, они оказываются куда страшнее суммы перечисленных проблем во всей этой статье
Зачем было по-умолчанию реализовывать удаление именно завершающих пустых строк из результирующего массива? Как будто нужно либо удалять все пустые строки, либо не трогать вообще
Возможно, это объясняется тем, что trailing comma — удобная и сверхпопулярная вещь, а вот leading comma практически не встречается
Прямо сейчас у меня в initramfs нет баша, вообще. И очень вряд ли, что у вас иначе. А шелл-скрипты там есть и бывают в немалом количестве. У меня там ash, вроде
А ещё в дебиане вызовется dash, если ваш шебанг вдруг окажется так весьма популярным #!/bin/sh вместо #!/bin/bash
А ещё нередко встречается busybox в эмбеде, там тоже ash
А ещё нередко в прод ставят контейнеры, в которых до предела минимизируют окружение. И если там и нужен шелл, то им тоже будет не баш
А иногда даже бывает нужна совместимость с zsh, в котором есть массивы, но доступ в которых работает иначе. Это, например, важно для скриптов автокомплита. Там вариант с bash -c несколько затруднителен
В первую очередь – того, кто читает код. Потому что чем больше стена, тем труднее верно оценить её (во всех смыслах). Страдает не только процесс разработки, но и ревью, и аудита. Да даже банально будут страдать коллеги, которые работают с тобой в общей кодовой базе
Анализатор прекрасно видит несоответствие отступов отсутствию фигурных скобок. Он это безошибочно подчеркнёт как misleading indentation
А первый пример банально нереалистичен и рассматривать его нет никакого смысла. Потому что стейтменты обычно длинные и в одну строчку, да ещё и с ифом обычно не влезают – не пройдут ревью
Я вот лично попадал в другую нелепую ситуацию:
if (foo) {
} else if (bar) {
} else if (baz) {
} { // потерял else
}
Что предложите ещё запретить, чтобы решить ещё и эту проблему? Или, может, всё же, запреты это не панацея?
Ещё есть пакеты Flatpak, которые изолируют приложение, но вообще-то могут быть созданы кем угодно
Так и deb-пакеты может кто угодно. Суть не в сборке, а в репозитории и мейнтейнерах. Не доверяете Flathub? У RedHat есть свой репозиторий flatpak-пакетов от их мейнтейнеров
Сделайте фигурную скобку { обязательной после 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 рабочих часов. Либо просто ежедневно работаем меньше – пишем меньше фич
Это уже не статья, это почти полноценная справка. Мне кажется, если контент статьи как-то удачнее сгруппировать или расставить теги над каждым топиком, большая часть этих топиков была бы уместна даже на cppreference в каком-нибудь самостоятельном разделе
Вы ошиблись с картинкой, исправляю
C++ in 21 day
Писать акроним капсом – это всё ещё плохая идея безотносительно подчёркивания, поскольку имя переменной капсом пересекается с общепринятым неймингом констант или макросов
И возможно, что на практике такой нейминг в процессе ревью нанесёт даже больший ущерб, чем ifndr из-за подчёркивания
Что насчёт трудоёмкости поиска этих ошибок?
Это противопоказано в общем случае в basic.lval для любых типов (кроме небольшого списка исключений в начале абзаца, в который пара из
int32_tиint64_tне входит):Для нетривиальных желаний есть
std::bit_castиstd::start_lifetime_as. Однако первый потребует копирования, а в допустимости применения второго в данном случае я вообще не уверенИ хотя мы достоверно не знаем, прав здесь ИИ или нет (это может зависеть от фактических типов аргументов), чушь нагаллюцинировал тут, всё же, не он, а вы
Да, такое понятие как 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-механизмовКакие последствия вы выбираете вместе с этим "решением"?
Падение производительности: замену маленьких скоупов критических секций, которые можно было выразить раньше на те большие скоупы, что можно с вашим предложением?
Снижение надёжности и безопасности кода: из-за перехода на хрупкие ручные lock/unlock, которые можно забыть вызвать из-за невнимательности или необработанного внезапного исключения?
Предпочтёте генерировать под это дело уродливые вымученные конструкции вроде такой?
Я отвечу за вас
Вы своим "гениальным" решением выбрали одновременно все варианты. Все плохие варианты. Потому что кто-то в вашей кодовой базе в одном месте предпочтёт потерю производительности, в другом кто-то предпочтёт хрупкий небезопасный код, а где-то ещё кто-нибудь предпочтёт обойтись просто уродливой конструкцией, вызывающей загадки на ревью и мистические слухи среди стажёров
Поздравляю, вы только что сделали код на несколько порядков более хрупким, багованным и сложноподдерживаемым... Там, где его и так было сложно отлаживать, где анализаторы практически не помогают — в критических секциях
И всё ради того, чтобы... Что? Чтобы убрать необходимость проверки от... анализатора, который у вас и так должен быть настроен в проекте по тысяче других веских причин? От линтера в IDE, который у вас и так наверняка есть из коробки? Или от специального флага компиляции компиляции, который тривиально добавить в сборку?
Так вот, иногда маленькие необдуманные дизайн-решения и запреты выливаются в огромные баги на ровном месте. Иногда, если совсем не думать, они оказываются куда страшнее суммы перечисленных проблем во всей этой статье
Возможно, это объясняется тем, что trailing comma — удобная и сверхпопулярная вещь, а вот leading comma практически не встречается
Вообще, по секрету, в баше есть ещё один вариант (или, скорее, их семейство):
И иногда это единственный доступный вариант, если утилита хочет шелл-команду одной цельной строчкой. Как
script, напримерПрямо сейчас у меня в initramfs нет баша, вообще. И очень вряд ли, что у вас иначе. А шелл-скрипты там есть и бывают в немалом количестве. У меня там ash, вроде
А ещё в дебиане вызовется dash, если ваш шебанг вдруг окажется так весьма популярным
#!/bin/shвместо#!/bin/bashА ещё нередко встречается busybox в эмбеде, там тоже ash
А ещё нередко в прод ставят контейнеры, в которых до предела минимизируют окружение. И если там и нужен шелл, то им тоже будет не баш
А иногда даже бывает нужна совместимость с zsh, в котором есть массивы, но доступ в которых работает иначе. Это, например, важно для скриптов автокомплита. Там вариант с
bash -cнесколько затруднителенЭто не всегда решает автор скрипта
В первую очередь – того, кто читает код. Потому что чем больше стена, тем труднее верно оценить её (во всех смыслах). Страдает не только процесс разработки, но и ревью, и аудита. Да даже банально будут страдать коллеги, которые работают с тобой в общей кодовой базе
Речь про этот пример из статьи:
Анализатор прекрасно видит несоответствие отступов отсутствию фигурных скобок. Он это безошибочно подчеркнёт как misleading indentation
А первый пример банально нереалистичен и рассматривать его нет никакого смысла. Потому что стейтменты обычно длинные и в одну строчку, да ещё и с ифом обычно не влезают – не пройдут ревью
Я вот лично попадал в другую нелепую ситуацию:
Что предложите ещё запретить, чтобы решить ещё и эту проблему? Или, может, всё же, запреты это не панацея?
Так и deb-пакеты может кто угодно. Суть не в сборке, а в репозитории и мейнтейнерах. Не доверяете Flathub? У RedHat есть свой репозиторий flatpak-пакетов от их мейнтейнеров
Да, прокинуть переменную для дисплея и сокет от xorg/wayland – это неподъемная задача, конечно. Никто не справится:
Ах да, не забудьте в контейнере поставить само приложение и шрифты к нему, конечно. Это тоже нетривиальное знание, знаете ли
Вас никто не заставляет так делать. Современный chmod вполне принимает опции в формате
chmod u=rwx,g-w,o+xА ничего, что статические анализаторы в современном мире и так подчёркивают жёлтым ошибочные конструкции? Нет, действительно, давайте уж вместо этого писать такие полотна, убивающие early return:
Ну а кому не будет приятно увидеть такой шедевр искусства в своей кодовой базе?
А как автор объяснит наличие таких специальных случаев в так называемом "нормальном пистолете"? Там аж целое специальное ключевое слово придумали, чтобы разрешить компилятору собирать такие специальные случаи
Вы не поверите, но в современном мире это так и работает. Движки баз данных знают, что такое биндинг параметров и поддерживают его. И современные ORM под капотом это используют
Во-первых, обосную значимость второго. Иметь возможность форвардить переменную, не задавая и не вычисляя её значения – это очень полезно и наглядно. Намного нагляднее, чем
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 рабочих часов. Либо просто ежедневно работаем меньше – пишем меньше фич