Не, я имею в виду, что какой смысл сравнивать реализации на интринсиках, если фактически ты будешь сравнивать ручную ассемблерную реализацию с такой же ручной ассемблерной, просто завёрнутую в другой язык. Нас ведь интересует, насколько хорошо компилятор оптимизирует обычный код.
компиль-тайм ошибки гораздо менее опасны, чем рантаймовые и в этом плане сишный компиль не многим тупей адового. скриптовый двиг читает скрипт и исполняет соответствующие асм команды. короче, аким дикобразом одно переводится в другое тут не важно - в любом случае переводится на одном проце эта команда есть, а на другом её нет и там заместо интринзика компиль молотит подстановки + компиль может чекать входные данные и/ль подлепить рантайм чекер + в бинаре может стоять переключатель, кой решает когда подключать интринзик, а когда его замену. ну-и, последнее == да, асм вставки могут юзаться для сокрытия неэффективности компиля.. Тут уж о глубинных причинах такого подхода нужно вопрошать у тех прогеров. если б секта ржавого играла бы честно, то ансейфы были бы для них табу
Неа, интринсики вставляются в код как есть, компилятор не имеет права их заменять. Он вставляет только сопутствующий код для передачи параметров в инструкцию интринсика. То есть, на интринсиках ты пишешь чуть-чуть выше, чем на сыром ассемблере. Единственное, что компилятор откажется собирать - если ты вставляешь интринсик из другого набора инструкций (например, собираешь под ARM, а пытаешься вставить интринсик от x86). Если же собираешь под конкретный процессор, а он не поддерживает интринсик - то варианта два: или соберёт именно с ним, или не соберёт. Но никогда не заменит его на аналог. Во всех остальных случаях в бинарнике ты увидишь именно ту инструкцию, которую написал. Так в сишных бенчмарках тоже юзают интринсики. И получается, что ты одну и ту же ручную ассемблерную реализацию сравниваешь с самой собой (иногда даже один к одному), просто вызванной в другом языке. Ну такое ~ А ансейф - это такой же равноправный элемент языка. Всегда будут случаи, когда что-то невозможно выразить в безопасной парадигме. Например, доступ к KUSER_SHARED_DATA. В винде он всегда лежит по фиксированному адресу, но т.к. сам факт наличия такого адреса ниоткуда не следует (ты его не выделял, он просто взялся изниокуда) - раст никак не может убедиться, что доступ к нему безопасен. Поэтому ты просто говоришь ему: "Беру ответственность на себя, разыменовывай" - и нет проблем. И в ансейфе, на самом деле, разрешено далеко не всё. Например, правила заимствований продолжают работать и для сырых указателей, поэтому устроить гонку в ансейфе можно только намеренно, если задаться целью.
Гораздо тупее, объявил ты range тип от 0 до 100, а потом к нему попробовал прибавить 101 в процедуре, это переполнение будет отловлено в компайл тайме. В Цэ ты никакой абстракции над целыми числами объявлять не можешь, есть 32-бита под целое и живи с этим как хочешь. Или объявил ты два типа: Цельсий и Фаренгейт, и переменной с Цельсием не можешь присвоить Фаренгейты, не вызвав функцию конвертации одного в другое. А в Цэ и то и другое - обычные 32-битные целые, которые всегда можно перепутать, если ты не элитный цэшный дед, который никогда в своей жизни ни одного указателя не прое... ну ты понял. --- Сообщение объединено, 21 ноя 2023 --- Я помню, оголтелые адепты захейтили бедного автора на секундочку самого быстрого веб-сервера на ржавом до того, что он самовыпилился из оупенсорса. Многие там считают, что ансейф для рукоблудов, а правые во всем ржавые поцоны только в сейф кодах живут.
Ага, такое было - это про ActixWeb. Вообще, я в детали не вдавался - возможно, хейт был и по делу: например, его ближайшие конкуренты - Rocket и Hyper - показывают плюс-минус такую же скорость, а написаны без ансейфа (вроде). Тут и сообщество можно понять: веб-сервера - это буквально то, где ансейфа хочется по минимуму) Но тут каждому своё - я не считаю его чем-то страшным (да и нужен он довольно редко). --- Сообщение объединено, 21 ноя 2023 ---
сишка - это логическое продолжение асма, она делает минимально возможный оверхед и, зачастую, ручной асм код сишку обогнать не может и дело даже не в умности компиля, а в том, что за десятки лет в компиль напихали наиболее скоростные костыли, кои и пихаются в бинарь .. однако, иногда асм вставки нужны. у ржавого же фетиш сейфовых функций и тут возникает простой Вопрос: аким дикобразом этого можно достичь??? * железки с дыреньками, а самые топовые игровые жестянки тупо пашут в аварийном режиме - если раньше разгоняли железки дурачки энтузиасты, то сейчас их такими продают. * на кой 4ёрт вообще писать на ржавом, если все оси и вся их оснастка на сишке??? невозможно использовать никакое апи без ансейф вызовов. вот и получаем очередную кашу из топора если используется адреса из ниоткуда - это уЖО и есть потенциальные забеги. вот и получаем вилку.. * задаём больше ограничений - РАСТёт несовместимость со средой исполнения. * пишем ансейфом а-ля сишка, так смысл сейфа РАСТёт в зеро. ЗЫ.. мы можем вспомнить чем окончилась борьба с дыреньками процев - все просто офят ту защиту ко всем 4ертям ёклмн, акие абстракции в реалтайме.. иль это так необходимо переводить цельсий в фаренгейты в ядре оси??? а если Тебе так приспичило - задай функи (одна считает ц, другая ф)... int F_degs(int val, int code_4_op){ int static deg = 0; if code_4_op == __GET__ { return deg; } if code_4_op == __ADD__ { return val + deg; } //................. }
А я и не спорю. Но баланс возникает сам собой: тебе ансейф зачастую просто не нужен. Необходимость в нём возникает, когда надо или работать с сырой памятью и указателями, или с внешними либами/API на других языках. Или непосредственно с железом. И всё это требуется нечасто. А где требуется, ты заворачиваешь всё в безопасные обёртки, и дальше работаешь с ними. То есть, какой-то ансейф остаётся, но он локализован в отдельных модулях, и ты уже знаешь, что вон там условный format C: произойти может, а вон там, где не ансейф, не может. Ну, во-первых, он прикольный. А во-вторых, мы считаем окружение условно безопасным: если там есть баги - мы с ними ничего не сделаем. Но зато в нашем собственном коде от наших собственных багов немножко убережёмся. А её в расте просто не отключишь. Там нет годмода, который разрешит делать всё. Ты хоть обложись ансейфом, но пока не удовлетворишь компилятор - бинарник не соберёшь. И на самом деле, порог вхождения в раст НАМНОГО выше, чем даже в плюсы с их шаблонами. У тебя просто не получится писать код так, как ты это делал в других языках. Это сродни поговорке про мышей и кактус - писать на расте сложно, ты буквально страдаешь, но что я заметил по себе - код практически не надо отлаживать. Как только rust-analyzer в вскоде перестал ругаться, ты можешь быть уверенным, что сейчас ты запустишь проект, и он будет работать именно так, как тебе надо, с первого раза. В плюсах это постоянный цирк: написал простыню, запустил сборку - на подстановке какого-то шаблона не собралось. Переписываешь, собираешь - не собралось (а редактор тебе фиг подскажет, потому что он сам не знает, валидный у тебя код или нет). Переписываешь, собираешь, успешно: запустил - упало.
а что значит "не нужен"? куча прог больше, чем на половину, состоит из вызовов апи. более того, практически любой яп подходит для лабания обёрток и раст в этом плане ничего интересного не добавляет. опять же повторюсь (мы с тобой уже на эту тему говорили) - основная защита всегда строится на физическом разделение потоков данных, поэтому заниматься графоманством особо "секурного" кода попросту излишне.. это, к примеру, во времена той же энтишки 4.0 железки были дорогие и в один сервач пихали всё, что ни попадя. а ща ханипот на ханипоте и каждый целевой сервак получает узкую функцию ох, уж эти растолюбы - отлаживать не надо, но они страдают проблемность отладки в сущности от яп-а не зависит == всё упирается в уровень задачи и текущих возможностей прогера. в расте просто процесс отладки во многом перепихнули в задачу компиляции. Однако, во множестве случаев от страданий в компиль-тайме толку-то мало. к примеру, очень любят рассказывать, молДе вот яп порождает сейф-тред код, где прям невозможны забеги потоков. просто берём любой комп и начинаем его гонять по стресс тестам и довольно легко найти режимы, где доморощенный thread-safe попрёт вкривь и вкось - в лучшем случае упадёт без доп сюрпризов, а в худшем и железки пожгёт
UbIvItS, в целом, ты прав: безопасные обёртки можно писать на любых языках. Просто в расте возможностей обезопаситься больше. Например, хотим пройти по контейнеру и удалить какие-то элементы, которые удовлетворяют условию. Само условие для простоты опустим, пусть удаляет все подряд. Вот как это можно написать: Код (C++): // Вариант 1: for (const auto& entry : container) { container.erase(entry); // Boom! } // Вариант 2: for (auto it = container.begin(); it != container.end(); ++it) { container.erase(it); // Boom! } // Вариант 3 (уже сложнее, выглядит безопасно, но всё равно невалидно): for (auto it = container.begin(); it != container.end();) { auto current = it; ++it; container.erase(current); } // Вариант 4 (правильный): for (auto it = container.begin(); it != container.end();) { it = container.erase(it); } (и в C++20 появился удобный erase_if, которому можно передать предикат). И каждый из вариантов на первый взгляд не кажется плохим. Мол, идём по элементам, удаляем, вроде всё нормально. И оно даже может не падать или может падать не всегда. И ты никак от таких проблем не защитишься. Или проблемы, связанные с обращением к уже перемещённому объекту или с частичным перемещением. Например, вот есть код. Сможешь найти в нём проблему? Код (C++): struct Klass { void* buf; Klass() : buf(malloc(...)) { } ~Klass() { close(); } Klass& operator = (Klass&& klass) noexcept { close(); buf = std::exchange(klass.buf, nullptr); return *this; } void close() { if (buf) { free(std::exchange(buf, nullptr)); } } }; Klass klass1; Klass klass2; klass2 = std::move(klass1);
чой-то странное.. функа эрейс что именно возвращает??? в данном случае бросается в глаза нульптр - зачем он там??? --- Сообщение объединено, 21 ноя 2023 --- вот один из примечательных примеров багов в расте.. в глобальном плане побороть такие ошибки невозможно - систему всегда можно поднагрузить и синхра пойдёт танцевать в пьяную --- Сообщение объединено, 21 ноя 2023 --- клоуз, конечно, тоже интересно сидит
Итератор, который смотрит на следующий элемент после удалённого. Например: std::map<Key,T,Compare,Allocator>::erase - cppreference.com Обнуляем указатель в перемещаемом элементе, иначе в его деструкторе освободится перемещённый указатель, которым владеет уже другой владелец. Например: Код (C++): Klass func() { Klass klass1; // Выделил буфер и владеет им Klass klass2; // Выделил буфер и владеет им klass1 = std::move(klass2); // klass1 освобождает свой буфер, забирает буфер у klass2 и обнуляет его указатель на буфер return klass1; // Здесь вызывается деструктор klass2. // Если указатель в нём ненулевой, то его close() сделает указателю free(), // освободив буфер, которым владеет уже klass1. } Принимая владение чужими ресурсами, надо освободить свои. Пока проблему не нашёл - какие ещё предположения? Кстати, вопрос открытый: все, кто тут топит за безопасность средствами программиста, а не языка, дружно пробуем найти в этом коде баг. Представьте, что вы проводите code-review, встретили такой код. Что с ним не так?
как-то криптовано выглядит, можь лучше так.. free(buf); buf = klass.buf --- Сообщение объединено, 21 ноя 2023 --- тоже плохая схема - итератор имеет смысл изменять явно. --- Сообщение объединено, 21 ноя 2023 --- чтобы что-то обнаруживать, давай сначала криптованность снимем
Может и имеет смысл, но валидный код только тот, который в варианте 4. А напиши полную реализацию оператора перемещения, как ты его видишь
Очевидно, его писал рукожоп, ибо сырой указатель без счетчика ссылок никогда не выходит наружу из объекта владельца Это из-за таких багов у вас постоянно use-after-free в сишечке чтоле?
А если на плюсах? А мы и пишем обёртку над сырыми указателями, чтобы завернуть его во что-то безопасное. Этим указателем может быть какой угодно ресурс (или группа ресурсов), требующий освобождения - указатель, хэндл или даже состояние. Да, довольно часто именно из-за подобных багов. Где-то что-то забыли проверить - и начинается цирк.
технически не везде должен быть счётчик ссылок = в основном достаточно проверить указатель (нуль али нет) зачем? логика понятна - дальше лень. я на плюсах в основном пользую оператор нью и классы юзаю редко - сишной стилистики вполне хватает. а слишком задвигать классами - ты свой же код потом перестанешь понимать, пч логика работы класса приводит к массе неявных преобразований
Ну, тогда неудивительно, что у вас там все постоянно течет и переполняется Еще раз: сырые указатели никогда и не при каких условиях не покидают объект владелец. Если такой функционал необходим, указатель заворачивается в структуру со счетчиком ссылок и удаляет себя сам, когда больше никому не нужен.
Так мы и пытаемся написать этого объекта-владельца. Например, нам CreateFile возвращает хэндл. Давай напишем RAII-обёртку для него, чтобы положить в неё хэндл и не беспокоиться, что он утечёт.