Как стать автором
Обновить
3
0

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

Отправить сообщение

Код осознаётся как ООП обычно через боль и страдание – в хорошем коде ты просто не замечаешь ООП, ведь его элементы использованы по необходимости и не травмируют тебя своей избыточностью. И да, фанатичное использование ООП закономерно ведёт к осознанию и боли, и страдания

Мне в голову просто приходит пример ядра линукса, который написан на чистом C

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

Даже разработчики libc++ наступали на это

Программисты вынуждены искать сторонние решения или подключать громоздкие фреймворки только для того, чтобы отправить HTTP‑запрос

А расскажите, как именно "громоздкий фреймворк" перестаёт быть таковым, когда вы его переносите в стандартную библиотеку?

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

Быть может, вы заблуждаетесь, считая эту вещь такой уж простой? Реальность такова, что "простое" решение может оказаться невостребованным, даже если оно является стандартным. Что важнее, вы уж определитесь, вы за производительность, требующей нетривиальных вещей вроде асинхронного ввода-вывода, или же вы за медленную простоту

Для чего в С++ auto и лямбды? Для чего синтаксический сахар с новыми формами цикла for?

Может быть, для решения тех самых базовых задач современного программирования? Что важнее, как раз эти фичи позволяют решать задачи эффективно с точки зрения производительности. Потому что без range based for, ваш код скорее всего окажется необоснованно неэффективным в силу того, что вам нужно в голове держать кучу информации. От существенной вроде того факта, что из-за strlen в условии можно получить квадрат до более тонких, как, например, как знание того факта, что беззнаковые индексы хуже оптимизируются компилятором ввиду наличия гарантий на поведение при их переполнении

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

Теперь получается, что, "написана ли программа на 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 зачастую имеет отрицательную стоимость для конечного потребителя

Думаю, вы можете попробовать запустить на нём waydroid

Информация

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