Pull to refresh
4
0

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

Send message

Теперь получается, что, "написана ли программа на C++ или нет", зависит не от её кода, а от набора внешних по отношению к ней входных данных

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

А если вам, например, нужен эффективный инкремент знакового счётчика, в этом случае довольно осмысленно завязаться на тот факт, что на современном оборудовании его переполнение займёт десятилетия. Но если вы выждали десятилетия, или запустили программу на инопланетном оборудовании и таки переполнили счётчик, внезапно сделав программу не программой на C++, то... ну, вас в меньшей степени будет волновать вопрос того, написана ли программа на C++

И дополнение. Ваш вывод не совсем верен. Согласно стандарту, написана ли программа на C++, всё же, зависит не совсем от внешних факторов. Как только UB стало неизбежно, программа ретроспективно перестаёт быть программой на C++. Т.е. никогда ею и не была, независимо от внешних условий

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

Замечу, что скамеру, убедившему бабушку следовать его инструкциям, вовсе и не требуется отключать никакие системы защиты в её телефоне

Давайте я переформулирую мысль из поста:

Картинка в 200 фпс в играх не являются достаточными условием для плавного геймплея, поскольку для него нужна ещё и обработка физики с высокой частотой

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

Вы же, прочитав текст, не вникая в его смысл, сформировали у себя в голове почти противоположный тезис "картинка в 200 фпс в играх не являются необходимым условием..." и оспариваете его, хотя он заявлен не был автором

Простите, а где вы увидели в тексте заявление о том, что эта разница незаметна? Там написано совсем не это

Я просто пытался сказать, что содержимое заголовка и текста абзаца не вполне соответствует бенчмарку (ибо дело не в согласовании signed/unsigned, а конкретно в unsigned, код с которым, предположительно, окажется медленнее, даже если везде будет unsigned)

Но если принять во внимание расширение Вселенной, то получается что световой луч пройдет большее расстояние за счет расширения самого пространства

Именно поэтому радиус наблюдаемой вселенной оценивается именно в ~46 миллиардов световых лет, а не в ~13.8

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

Только вот в современном мире почти везде используются two's-complement-числа, для которых это приведение типов — no-op. И, вероятно, бенчмарки показывают такую разницу не из-за разности типов, а из-за, собственно, unsigned, код вокруг которого, предположительно, хуже поддаётся оптимизации из-за допустимости его переполнения с точки зрения стандарта (в отличие от signed, переполнение которого не определено стандартом)

Тот же докер, чтобы поставить на убунту, нужно пройти небольшой квест

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

apt install docker.io

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

Я тоже люблю винду похейтить, однако... У меня винда ставится за несколько минут. Да, это не минута, и nvme у меня в ноуте по-быстрее будет, но, простите... А средний юзер действительно заметит оптимизацию скорости установки винды с пяти минут до одной, пока заваривает чай? Я уж молчу, что большинство юзеров будут ставить себе винду примерно ноль раз за всю жизнь

И действительно ли этот результат стоит потенциальных неприятных последствий переписывания логики установщика?

Ну да, ну да, кто-то пишет if(!result) вместо if (result != http::ok), чтоб потом узнать, что http::ok внезапно 200, а не 0. Не надо так

А вы http-коды храните в интах? Тогда у вас более серьёзные проблемы, чем обсуждаемая выше, до которой ещё дорасти нужно

Но вот чем принципиально для вас отличается if (opt.has_value()) от if (vec.isEmpty())?

Тем, что одно из условий положительно, а другое негативно. Главное, что оба выражены прямо так, как подразумевал программист. А вот стоит только взять if (!vec.isEmpty()) — здесь уже проблемы, описанные мной выше. Имеем ввиду наличие элементов, спрашиваем про отсутствие

Любой из двух следующих вариантов я считаю предпочтительнее:

auto has_element = !vec.empty();
if (has_element) /* ... */;
if (vec.count()) /* ... */;

Ещё лучше было бы, предоставляй стандартные контейнеры сразу оба — и empty, и has_value

Можно ещё сравнить std::optional и std::any

На самом деле нельзя. У них семантика разная, std::optional семантически предполагает отсутствие значения, это фича для конечных пользователей. А для std::any (как и std::variant) это вынужденная фича, необходимая для поддержания мув-семантики, а не для пользователей. Т.е. не предполагается, что пользователь будет пользоваться has_value/valueless_by_exception как нормальным интерфейсом, ему это в норме и не нужно. А соответственно и неявный каст не нужен — это не часть нормального повседневного интерфейса, так что ответ на вопрос...

Вас не смущает использовать разные способы проверки одного и того же?

Однозначно нет. Пользователь для std::any в норме не пользуется этим интерфейсом. Более того, в архитектуре даже худших среди кодовых баз, с которыми я работал, ещё не было столь серьёзных проблем, чтобы мне нужно было использовать "такой" std::any

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

Одинаково плохо читается и это:

if (страна.город.мэрия.зачем.так.много.вложенности.удалена == false)

и это:

if (not страна.город.мэрия.зачем.так.много.вложенности.удалена)


Если в первом случае отрицание далеко от непосредственного контекста, то во втором оно не на том месте и отделено от него синтаксическим шумом. А теперь просто сравните с

if (страна.город.мэрия.зачем.так.много.вложенности.неУдалена())

Может, настало время добавить немного выразительности в интерфейсы своих типов?

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

auto&& foo = страна.город.мэрия.зачем.так.много.вложенности; 
if (not foo.удалена())

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

Казалось бы, к чему я вообще о какой-то иерархии в этом вопросе и где здесь иерархия? Всё просто — рано или поздно мозг программиста начнёт любой if (something) обобщает до до некоторой общей абстракции. Не думаю, что у неё есть название, но я бы её выразил как if ok/if not ok. Когда мозг их обобщил, становится очень просто читать такой код. И дело не в его длине, а в том, что все подобные конструкции мозг начинает маппить на то самое внутреннее обобщение

Но при этом, когда вы местами используете if (opt.has_value()), местами if (ptr != nullptr), где-то if (result != http::ok), где-то ещё что-то... Мозг просто хуже это делает — вместо одного обобщения он работает лишь с частными случаями всюду. А исключения и частные случаи мозг не очень любит

Но почему мозг точно так же не может обобщить разные примеры выше?

Потому что ещё был упомянут альтернативный вариант проверки, if (opt != std::nullopt). И это просто ужасный, кошмарнейший пример, связанный уже не с длиной кода. Эта строчка подразумевает, что внутри ветки мы работаем с happy path. И автор имеет ввиду положительный путь исполнения, и код пишет под положительную ветку исполнения. Но в условии при этом он спрашивает буквально "не является ли путь негативным". Изначальное семантически чисто положительное условие превратилось аж в две инверсии. Имеем ввиду положительное — спрашиваем дважды негативное. И, хотя формально две инверсии не меняют семантику на негативную, мозг, видя инверсии и негативизацию в условии... считает их чётность... и считает крайне плохо, и сбоит. Видит отрицания — думает про unhappy path. В первую очередь, конечно. Во вторую таки считает чётность, откатывает в своей ментальной модели то, что навыдумывать успел... Много избыточной ментальной работы, много кеш-миссов в голове

И это ещё не всё. Теперь комбинируем все перечисленные проблемы вместе и получаем ещё одну: if (opt != std::nullopt) и if (opt.has_value()) выражают одну мысль, но совершенно разным языком, который не обобщить до одной понятной концепции в своей ментальной модели. И вместо того, чтобы читать произвольную ветку как if (ok), ваш мозг будет вынужден каждый раз дополнительно разбираться, что значит второй аргумент в сравнении, к какому "виду" сравнений оно относится, считать чётность инверсий и перепроверять себя, забивая ментальный контекст этой мусорной работой и утомляя вас

Чем больше вы утомляетесь, тем больше ошибок совершите, разных и в разных местах

Какой вывод? Выражайте мысли прямо, чтобы ошибаться меньше. И if (something) — самый простой способ выразить мысль прямо. Автор будет меньше раздумывать, каким именно правильным способом это написать (правильный останется единственным, если вписать в стайлгайд и распространить на всю кодовую базу), а значит и с меньшей вероятностью совершит ошибку из-за семантического насыщения. А пользователь тоже будет читать привычный паттерн на автомате, снижая риски запутаться при виде всех этих таких похожих визуально, но таких семантически разных != nullpt, != http::ok, != nullopt. Ой, а в средний закралась ошибка. А вы её заметили?

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

В таком варианте при прочтении кода лучше видны не стыковки.

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

if (SomeGlobalPointer == nullptr)
{
  delete[] SomeGlobalPointer;
}

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

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

И если в исходном условии зависимость лишь одна — SomeGlobalPointer, возможных исходов в ментальной модели тоже два — либо у нас SomeGlobalPointer, либо у нас не SomeGlobalPointer. Добавив же == nullptr, мы привнесли в ментальную модель ещё как минимум одну сущность. И не важно, что это неизменная часть условия

В нашей ментальной модели теперь сущностей, от которых зависит исполнение тела, стало в два раза больше — две. Также и возможное число исходов (где-то в фоне считаемое мозгом) увеличилось до четырёх — SomeGlobalPointer, не SomeGlobalPointer, nullptr, не nullptr

Это лишь увеличит когнитивную нагрузку при том же семантическом наполнении кода. Вы будете уставать чуть быстрее. Уставая же, будете совершать новые ошибки. Решали одну конкретную частную проблему — добавили новых неочевидным для себя образом

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

TL;DR: пайп позволяет визуально отделить вход от выхода и совершать меньше действий в процессе правки команды

В своём рассуждении, вы ловко выкинули из примеров команд pattern:

grep pattern file
cat file | grep pattern

А ведь чаще всего вы будете нажимать стрелку вверх и менять именно паттерн (или даже весь grep) при том, что имя файла будет оставаться фиксированным. Но каждый раз, нажимая стрелку вверх, ваш курсор будет оказываться в конце строки, т.е. рядом с file, и вам каждый раз нужно будет перепрыгивать его, чтобы добраться до паттерна, который будет за сессию работы меняться куда чаще. Лично мне лишние прыжки через имя файла удовольствия не доставляют, поэтому я сознательно использую cat. Вы, конечно, можете опротестовать и предложить мне более эффективную (с точки зрения машины) альтернативу:

< file grep pattern

Но. Стрелку легко перепутать, уничтожив файл. Во-первых, стрелки рядом на клавиатуре; во-вторых, даже будь они далеко — перепутать их всё ещё можно. Ещё здесь нет явного и заметного разделителя, который было бы удобно искать глазами — имя файла миксуется с командой; в случае с cat такой разделитель есть — пайп. Да и просто же выглядит довольно уродливо

Так я и проверил — сразу привёл релевантную ссылку с меткой wontfix в своём исходном сообщении, более релевантных страниц в выдаче не вижу

А что значит -march=native в контексте wasm? Кажется, даже разработчики в некотором недоумении

А в чём проблема? Если операционки — это линуксы, а даты их релизов не отличаются на года, то держать под системой контроля версий единый набор конфигов для них — это обычно не проблема. Ну, допустим, будет ли вам невыносимо плохо от осознания того факта, что у вас среди конфигов лежит что-то под неустановленную программу?

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

Information

Rating
Does not participate
Registered
Activity