Pull to refresh

Comments 313

Пример с std::optional просто фееричен по уровню идиотизма. Тип optional и не предоставляет гарантии, что он всегда содержит какое-то значение. На это даже намекает само название типа. Претензии к операторам * и -> только показывают, что автор понятия не имеет о контрактах функций. Если мы посмотрим описание этих операторов на cppreference, то мы увидим, что
The behavior is undefined if *this does not contain a value.

Разработчик обязан удовлетворить предусловия функции перед ее вызовом. В данном случае он обязан убедиться, что optional содержит значение, иначе получит неопределенное поведение. Как бы того ни хотел автор, но в любом языке программирования функции будут иметь предусловия, явные или неявные. Не все предусловия можно проверить. Не все проверки предусловий осуществимы за разумное время. Не все осуществимые проверки стоят того, чтобы быть выполненными. Сомневающихся прошу ознакомиться с данным документом. Всегда придется искать какой-то разумный компромисс. Язык C++ нацелен на максимальную производительность, поэтому написанные на нем библиотеки часто перекладывают на пользователя выполнение дополнительных проверок.

Хотя я не отрицаю, что современный C++ превратился в какого-то монстра и использовать его корректно очень и очень сложно, чтобы бросаться переносить существующие наработки, надо сначала создать хорошую замену C++. Языки с VM и сборщиком мусора не являются заменой по определению. Rust выглядит довольно перспективно, но тоже не без проблем.
Разработчик обязан удовлетворить предусловия функции перед ее вызовом.

Логично, но люди совершают и будут совершать ошибки. Даже если программистов начнут расстреливать за UB, то ситуация принципиально всё равно не изменится: ошибок, вероятно, станет всё-таки поменьше, но и цена на разработку взлетит.


И насчёт "нацеленности на максимальную производительность" тоже, вроде как, справедливо. Но если быть честным, то насколько замедлится софт написанный на С++, если там включить проверки в std::option и векторе? Гадать дело неблагодарное, но я что-то думаю, что далеко не везде это будет заметно.


В этом плане мне намного больше нравится подход раста, где по умолчанию такие проверки как раз есть. А если вдруг мы видим, что в этом конкретном месте упираемся в производительность, то всегда можно использовать "unchecked" версию соответствующего метода (и желательно обложиться тестами). Да, в С++ есть at, но честно говоря не встречал чтобы им активно пользовались. Может мне не повезло, конечно. Но зачастую при выборе между доступом по индексу через квадратные скобки и "неуклюжим" вызовом get_unchecked выберут первое. В общем, я за "правильные" умолчания.

Как правило в реализации std::optional стоит assert(), который срабатывает в debug билдах.
Но вот у клиента и тестировщика как правило билд не debug. Поэтому assert «никто» не увидит
Ну, с вероятностью 99.99% это всё равно приведёт к аварийному завершению программы. А если есть Дося, то зачем платить больше.

Ну как бы это неопределённое поведение. Да, скорее всего упадёт, но полагаться на это не стоит.

Логично, но люди совершают и будут совершать ошибки
то то я смотрю в безопасных питонах и жсах люди совсем никогда не допускают ошибок…
И насчёт «нацеленности на максимальную производительность» тоже, вроде как, справедливо. Но если быть честным, то насколько замедлится софт написанный на С++, если там включить проверки в std::option и векторе?
тут не может быть «на полшишечки», либо яп нацелен на максимальную производительность, либо нет.
Да, в С++ есть at, но честно говоря не встречал чтобы им активно пользовались. Может мне не повезло, конечно
вам не повезло
то то я смотрю в безопасных питонах и жсах люди совсем никогда не допускают ошибок…
Ошибок управления памятью всё же не совершают
На Питоне тоже можно получить ошибки управления памятью. Например, попробуйте запустить процесс через multiprocessing, передав ему в качестве аргумента NumPy массив, который освободится сразу после запуска процесса. Второй процесс получит вместо данных случайный мусор.

Но это ещё не всё. Обратная сторона отсутствия возможности управления памятью — это существенное замедление работы в рантайме. Попробуйте распарсить XML, используя любую библиотеку Питона, а потом сделайте то же самое на С++ через библиотеку RapidXML++ — разница в производительности будет отличаться в сотни раз! Почему? Да потому, что в Питоне на каждую маленькую строчечку — имя атрибута, значение атрибута, имя ноды, содержание ноды — будет хоботня с памятью, а RapidXML++ выделяет большие куски памяти и освобождает её только в самом конце и сразу, что даёт самый быстрый в мире парсинг. И даже если обернуть RapidXML++ в Питон, то ничего это не изменит, потому что Питону всё равно для парсинга придётся создавать все эти маленькие строки-переменные, чтобы можно было сравнивать их значения с искомыми.
Например, попробуйте запустить процесс через multiprocessing, передав ему в качестве аргумента NumPy массив, который освободится сразу после запуска процесса.
Это будет происходить в любых местах, когда управляют памятью несколько независимых систем.
И даже если обернуть RapidXML++ в Питон, то ничего это не изменит, потому что Питону всё равно для парсинга придётся создавать все эти маленькие строки-переменные, чтобы можно было сравнивать их значения с искомыми.
RapidXML++ использует свой менеджер памяти? Если строки только сравниваются на равенство, то можно обойтись без копирования, используя смещения.
Это будет происходить в любых местах, когда управляют памятью несколько независимых систем
В таком случае заберите свои слова обратно, потому что — вот Пайтон, вот я использую стандартную библиотеку, вот получаю мусор (в переводе на ваш словарь — «ошибку управления памятью»)
RapidXML++ использует свой менеджер памяти? Если строки только сравниваются на равенство, то можно обойтись без копирования, используя смещения.
Действительно, RapidXML++ определяет свой менеджер памяти. Но чтобы доставить эти данные в Пайтон (мы ведь говорим о парсере, правильно?), то для каждого узла, атрибута, имени атрибута и т.д. придётся создать маленькие переменные строки, а Пайтон не умеет создавать переменные, не выделяя под них память. В итоге разница в скорости — стократная, лично замерял для всех доступных библиотек. А всего лишь надо было отказаться от хоботни с памятью.
В таком случае заберите свои слова обратно, потому что — вот Пайтон, вот я использую стандартную библиотеку
Во-первых, это не стандартная библиотека. Во-вторых, данная ошибка вызвана кодом написанным на си, и исправить её переделав python код не получится.
придётся создать маленькие переменные строки, а Пайтон не умеет создавать переменные, не выделяя под них память
Вы говорите так, словно плюсы умеют работать с переменными, под которые не выделена память.
Во-первых, это не стандартная библиотека
Она идёт в стандартной поставке Python 3. Заходим сюда, замечаем заголовок: «Documentation » The Python Standard Library » Concurrent Execution». Ниже: «multiprocessing — Process-based parallelism»
Во-вторых, данная ошибка вызвана кодом написанным на си
Стандартный интерпретатор (ака cpython) вместе со стандартной библиотекой на треть написан на си (можете зайти на гитхаб и увидеть это в статистике. В частности, когда вы работаете со строками или списками, то вызываются си функции.
Вы говорите так, словно плюсы умеют работать с переменными, под которые не выделена память.
Мой поинт был в том, что в Пайтоне хоть убейся, но не сможешь управлять памятью эффективно. Иногда это приводит к тому, что ты не можешь даже XML распарсить за вменяемое время. С другой стороны, в С/С++ ты можешь создать свой эффективный менеджер памяти (RapidXML++) и даже выделить переменное количество памяти на стэке (alloca()), не дёрнув ни один пайтоновский или системный вызов.
Она идёт в стандартной поставке Python 3.
Вы говорите про multiprocessing, я говорю про NumPy.
Мой поинт был в том, что в Пайтоне хоть убейся, но не сможешь управлять памятью эффективно.
Кроме сборки мусора, между ними есть и другие различия, такие как типизация и интерпретация/компиляция. И если убрать эти различия, то преимущества ручного управления памятью либо уменьшаться, либо, в некоторых случаях исчезнут. Вот пример похожей задачи. Можно заметить, насколько сильно различаются даже задачи на одном языке, когда C++/g++ (Boost.PropertyTree) проигрывает как Ruby, так и другой реализации на C++. Как видите, некоторые реализации со сборкой мусора могут быть достаточно быстрыми.
Вы говорите про multiprocessing, я говорю про NumPy.
Ну хорошо, допустим — мне лень проверять этот случай для не-NumPy. Но ведь в основе переменных NumPy лежит тот же reference counter, который забирается сборщиком мусора, когда на переменную никто не ссылается. Почему же Python multiprocessing не сохранил ссылку на передаваемый аргумент и позволил сборщику мусора забрать данные, которые нужны другому процессу?

Попробую и тут выкрутиться ) «Ошибок управления памятью всё же не совершают» — так вы хотите сказать, что это утверждение верно только для стандартной библиотеки? Интересно, ведь одно из самых больших преимуществ Пайтона именно в том, что у него полно сторонних библиотек на все случаи жизни. OpenCV, TensorFlow, Panda3d, NumPy, SymPy, Matplotlib etc.
И если убрать эти различия, то преимущества ручного управления памятью либо уменьшаться, либо, в некоторых случаях исчезнут
Возможность ручного управления памятью — это вообще одно из главных преимуществ С++. В частности, в геймдеве и реал-тайм системах. Для бизнес-проектов мне сложно представить зачем использовать С++.
«Ошибок управления памятью всё же не совершают» — так вы хотите сказать, что это утверждение верно только для стандартной библиотеки?
Нет, это верно для кода, который полагается на сборку мусора, то есть того кода, который не вызывает free и вариации либо по той причине что не может — сам python, либо так как незачем — c.
Почему же Python multiprocessing не сохранил ссылку на передаваемый аргумент и позволил сборщику мусора забрать данные, которые нужны другому процессу?
Поскольку компилятор не проверяет код интерпретатора и библиотек на ошибки, а код достаточно нетривиален.

В ~25 раз медленнее быстрой C++-реализации - это "достаточно быстро"?

Кроме реализации на Ruby есть и другие языки со сборкой мусора: D, Go, Crystal и так далее. Там разницы в 25 раз нет. Кроме того, самые быстрые реализации на C++ имеют некоторые недостатки: либо это отсутствие части проверок корректности json, либо баги в реализации, либо определенные требования к железу и хранению данных, о чём сказано перед началом таблицы.

Как видите, некоторые реализации со сборкой мусора могут быть достаточно быстрыми.

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

UFO just landed and posted this here
Зависит от языка.

Я конечно далеко не эксперт в разных языках и парадигмах программирования, но тем не менее с этим утверждением я не согласен. Предусловия бывают самые разные, явные и неявные (подразумевающиеся). Как вы проверите, что вам передали указатель на последовательность символов, оканчивающуюся нулем? Если вызывающий код забудет дописать терминирующий ноль, вы этот факт никак не установите. Как вы проверите, что переданный в функцию указатель на структуру действительно указывает на такую структуру? Rust может гарантировать нечто подобное, но если данные пришли из внешнего источника их все равно придется валидировать. Любой самый защищенный язык при работе с низкоуровневыми абстракциями будет вынужден использовать unsafe секции кода. И там будут непроверяемые или сложнопроверяемые предусловия.
UFO just landed and posted this here
А если данные пришли извне? Если вам просто был передан указатель на область памяти, каковы будут ваши действия, чтобы осуществить данную проверку?
UFO just landed and posted this here

А теперь убираем из этой схемы завтипы и ничего не меняется :)

UFO just landed and posted this here

А зачем вы уберете проверку?

UFO just landed and posted this here

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

UFO just landed and posted this here

Не всегда только паттернами можно испортить код

Если уж так волнуетесь за сохранность проверки при оефакторинге, пишете тест до рефакторинга и запускаете после

UFO just landed and posted this here
А если данные пришли извне?
Либо это обеспечивается компилятором другого языка и вопрос только в передаче данных между языками, либо есть вероятность, что код формирующий эти данные уже успел наломать дров.
UFO just landed and posted this here
Потребую доказательство, что последний элемент массива — ноль. Это как раз просто.
А почему именно последний? Очень вероятно, что массив не будет заполнен полностью и ноль окажется где-то в середине, например — получение пакета по сети, обмен не обязятельно ведётся по 1500 байт.
Однако, обсуждаемый вопрос об operator* у std::optional к общению со внешним миром отношения не имеет.
Запросто, например, получение сетевого пакета без задержек, ситуацию моделирую тут, но почему бы и нет
std::optional<std::string> recv_str = server.recv();
if( recv_str ) { ...
} else { ...
}
UFO just landed and posted this here

Это пример неудачно разработанного API: часто используемая функция, могущая вызвать UB при случайном неправильном применении. Очень похожий тип есть в Rust и называется Option, но там он свободен от подобной проблемы: у него нет оператора разыменования. Есть либо извлечение с явной проверкой:


let x: Option<i32> = None;
if let Some(y) = x {

либо преобразование Option в другой Option через map:


let y = x.map(|v| v+1);

либо "разыменование", но с паникой (в C++ это могло бы быть исключение) в случае его некорректности:


let x: Option<i32> = None;
let y = x.expect("Bug: x contains no value");

Такой API гарантирует, что при некорректном использовании будет не UB, а всего лишь корректная runtime error. Сама вероятность некорректного использования также понижается.

Меня удивляет, что у std::optional в C++ нет ничего похожего на Option::map/Option::and_then. Вроде ж максимально логичные методы.

Для монадического интерфейса optional есть proposal, но да, это все идет довольно медленно.

В расте может быть unsafe unwrap_unchecked()

Можно, но этот метод надо в unsafe блок заворачивать. В обычном коде никто так не пишет (хотя бы из-за того, что набирать дольше). Ну а если кровь из носу надо, то да — можно. И это хороший подход так как unsafe и на ревью сразу видно и грепнуть можно или даже запретить на уровне модуля/библиотеки.

Сравните тот же Option из раста. Оно гарантирует либо панику, либо значение. В C++ в лучшем случае будет исключение, в худшем кто-то получит доступ к вашей памяти. То бишь не гарантирует практически ничего, кроме почти безопасного интерфейса. Об это и ведет речь автор. Пока не заведутся полноценные контракты из нового стандарта безопасность приложений останется под угрозой эксплуатирования таких проблем с памятью. Но и контракты не панацея и ждать их в лучшем случае лет 5.

Разработчик много чего обязан. Просто во всех языках кроме С ошибка программера приводит к неприятностям. А не к испорченным данным или взрыву на макаронной фабрике. (Ваш К.О. с опытом работы на Си с 1985 года.)
Неужели при вашем опыте вы не понимаете, почему в C++ все именно так? Некоторые проверки возможно осуществить, но ценой будут накладные расходы. Некоторые проверки невозможно осуществить за разумное время в принципе. Мне тут конечно уже рассказывали про некоторые языки, которые позволяют чуть ли не доказать соответствие значения аргумента функции заданным критериям. Про это я ничего сказать не могу, т.к. просто не знаю. Но по отношению ко «всем языкам», мне кажется вы лукавите. Что будет, если в C#, Java или Python функции бинарного поиска передать неупорядоченный массив данных? У функции есть два варианта: выполнить проверку и дать программеру по рукам, фактически превратившись в линейный поиск, или не выполнять проверку и получить… неопределенное поведение. Наличие нопределенного поведения при нарушении контракта функции — это компромисс. Хотите вы этого или нет, но как минимум во всех императивных языках у вас будут ситуации, когда выполнять проверки или дорого или вообще невозможно.

Хорошо бы контракты оформлять в типы. В правильной реализации у вас был бы класс SortedArray с непубличным конструктором и возможностью создать объект этого класса через вызов или sort (отсортировать имеющийся массив), или fromSortedArray (с проверкой, что переданный массив уже отсортирован). И, естественно, доступ к элементам такого массива возможен только на чтение.

std::string_view это вообще невладеющая обёртка над голым указателем и не может гарантировать время жизни данных. Автор олигофрен. Раст не может рассматриваться как замена плюсов из-за своих убогих архитектурных возможностей. Пока раст даже не может быть рассмотрен как системный язык — замена си. См. недавние хороводы вокруг драйверов ядра линукс.
Автор всего лишь показал, что человеки не способны программировать правильно, если компилятор за такие вещи не даёт по рукам. Была как-то инфографика выявления уязвимостей в проекте Chromium, так выходило, что каждые два дня находят одну уязвимость уже в течение 10 лет.
Автор предлагает запретить использование ножей и вилок потому что ими можно порезаться/уколоться?

То, что С++ идет фиг знает куда в попытках завернуть в красивые обертки как можно больше кода и вывести этот код из под контроля разработчика — это верно. С++ по своей структуре провоцирует на бездумное использование стандартных библиотек в надежде что «оно все само разрулит».

В чистом С все было честно — вот вам очень острый ножик, им можно делать что угодно, но легко порезаться если делать это без ума. В С++ ножик по прежнему остр, но эта острота тщательно скрыта от разработчика.
Мда, за предложение сделать без уязвимостей уже в карму срут…
Предлагается не связывать ножи и вилки вместе в разные стороны. А так же не снимать с лезвия рукоятку.
Не «без уязвимостей», а провоцирующие стиль «херак-херак и в продакшн».
Мда, за предложение сделать без уязвимостей

Сложные проблемы всегда имеют простые, легкие для понимания неправильные решения (С) сборник законов Мерфи, если не ошибаюсь. Впрочем, очередная статья про внезапный «нибизапастный сиплюсплюс», не знаю, стоило ли ее переводить — разве что в качестве затравки для очередного срача.
Да собственно, у меня мнение тоже совпадало с «не умеете готовить» из 1го абзаца, потому я начал читать статью. А что читать, что переводить почти одинаково, да и я подумал что не один такой.

Да и когда кому мешал хороший технический срач? =)
Так ведь статистика говорит сама за себя. Почитать хотя бы тот же Opennet — ни дня без закрытия найденных уязвимостей переполнения стэка или use after free.
Еще статистика говорит, что в open source коде, написанном на php, java, и js уязвимостей находят больше (а на python — примерно столько же), чем в коде на C++ (но меньше, чем на C, главным образом по причинам «исторического характера» — да и то, если взять в сумме C/C++ и эти языки, то получается сравнимо). Только там они другие — xss всякие, input validation, information disclosure. Уязвимости другие, результат тот же. Надо срочно менять и эти языки на что-то другое? А поможет ли это? Что-то я сомневаюсь. Да и авторы исследования тоже приходят к закономерно банальному выводу, что «it is not about the language itself that makes it any more or less secure, but how you use it».

На rustsec загляните тоже между делом — при всей мизерности объемов существующего кода на расте уязвимости типа double-free/use-after-free и прочий букет там находят регулярно, в том числе связанные с нарушением контрактов. Это не сферический «всегда-safe-код-в-вакууме» из мечт апологетов раста, а то, что обычный средний разработчик будет писать на расте в реальности. А вы, извините, как филин из известного анекдота со стратежным советом «надо, чтобы компилятор думал за человека, тогда все будет хорошо». Компиляторы тоже пишут люди.

Ладно, этот срач уже не первый (и по-видимому не последний), все аргументы всех сторон известны наперед, не вижу смысла в дальнейшем участии в очередной итерации :) Займусь чем-нибудь более полезным, поправлю вон очередной баг в fheroes2.
Ну да, когда нам говорят, что у нас что-то плохо, давайте показывать пальцев на других :))) (Почему-то кроме Раста)
«it is not about the language itself that makes it any more or less secure, but how you use it»
Ну так Раст как раз и заточен на то, чтобы не было зависимости от but how you use it.
Надо срочно менять и эти языки на что-то другое? А поможет ли это? Что-то я сомневаюсь.
Вообще-то да. Например, php в поставке по умолчанию, уязвим к загрузке файлов на сервер, и для обхода врождённой проблемы могут потребоваться значительные усилия, так-как сам язык подразумевает вызов кода по имени файла напрямую.

XSS возможен, так как языки работают с ответом как со строкой, безо всякой проверки корректности. Будь ответ представлен не как строковой буфер, а как дерево, пускай даже с лёгкой возможность вставки сырых строк, то это уже существенно бы облегчило проверку кода, и снизило количество ошибок. В случае, если бы для использования сырых строк дополнительно проверялось бы компилятором, как например в rust, то эти ошибки стали бы ещё более редкими, так как новички не смогли бы элементарно пройти проверку валидации. Например, следующий код не скомпилируется
fn main() {
    let t = "Hello, world!";
    println!(t);
}
Это не сферический «всегда-safe-код-в-вакууме» из мечт апологетов раста, а то, что обычный средний разработчик будет писать на расте в реальности.

#![forbid(unsafe_code)] в корне проекта — и у вас в команде никто unsafe-ом дров не наломает.

вот сижу я, пишу библиотеку с кучей требований по перфу и памяти, приходится использовать всякие ухищрения типа буфера + спанов в него, на расте без unsafe я такое в принципе бы не смог написать. Сидя в пустыне дров не наломаешь, да…
приходится использовать всякие ухищрения типа буфера + спанов в него, на расте без unsafe я такое в принципе бы не смог написать.

Так, а вот про это можно поподробнее?

Так, а вот про это можно поподробнее?
ну в плюсах я спокойно могу написать и использовать что-то в духе такого минимального примера:
код
struct Foo {
    Foo(std::string s) :
        str(std::move(s)),
        view(str) // ссылка на тот же буфер, что и в str
    {}

    std::string str;
    std::string_view view;
};

Проблема описана тут. Самое забавное, что в Rust std::String не имеет SSO оптимизации, и по идее слайс должен быть корректен даже после мува.

Как бы с одной стороны да, с другой — я бы на ревью подобный код не пропустил бы, поскольку тут view очень легко может повиснуть.

Как бы с одной стороны да, с другой — я бы на ревью подобный код не пропустил бы, поскольку тут view очень легко может повиснуть.
А с какой целью вы ревьюите код? Проверить его на наличие ошибок или завернуть по подозрению, которое вам лень перепроверять?
UFO just landed and posted this here

Мне лень высматривать весь код, чтобы убедиться в том, что в нём нет операций, которые меняют str, и я глубоко сомневаюсь, что люди, которые с этим кодом будут работать в дальнейшем, будут помнить, что это самоссылающаяся структура. Даже просто поменять тип str на const std::string было бы лучше.


И, кстати, конкретно в этом коде я вообще не вижу смысла в поле view. Оно и так будет инвалидировано при любом операции, затрагивающей str, а создать string_view из std::string можно в любой момент, это очень дешёвая операция. Так зачем его вообще хранить?

Мне лень высматривать весь код
представьте что вы реализовали какую-то нетривиальную функциональность, выложили код на ревью, и получаете от коллеги отзыв «я не вчитывался конечно, но мне кажется что ты где-то мог накосячить, переделай». Понравится?
Даже просто поменять тип str на const std::string было бы лучше.
ну я так и сделал по итогу
И, кстати, конкретно в этом коде я вообще не вижу смысла в поле view
ну это же сильно упрощенный пример, в реальном коде куча вьюшек на разные диапазоны внутри буфера.
UFO just landed and posted this here
И я считаю, что это делает мой код (и статью) лучше, чище и понятнее.
логично, ведь задача ревью как раз улучшить код, который попадет в базу. Вот замечания с предложениями (нераздельно!) эту цель преследуют. Или даже вопросы, иногда они действительно указывают на неочевидные моменты. А вот задачи «не допустить код» у код ревью нет и никогда не будет — код ведь с какой-то целью пишется, верно?

И когда я вижу комментарии а-ля «я бы на ревью подобный код не пропустил бы» и «мне лень высматривать весь код, чтобы убедиться...» я делаю вывод, что у человека синдром вахтера и ЧСВ.
И когда я вижу комментарии а-ля «я бы на ревью подобный код не пропустил бы» и «мне лень высматривать весь код, чтобы убедиться...» я делаю вывод, что у человека синдром вахтера и ЧСВ.

То есть недопущение в кодовую базу кода, корректное использование которого требует нелокального соблюдения важного инварианта, который нельзя зафорсить компилятором и потому почти гарантированно создаст проблемы при сопровождении — это ЧСВ?

То есть недопущение в кодовую базу кода,… — это ЧСВ?
нет, это синдром вахтера. ЧСВ вы проявили в «мне лень проверять, я проще заверну» — я не могу воспринимать это иначе как неуважение к коллегам.
корректное использование которого требует нелокального соблюдения важного инварианта
это вы из головы взяли, у меня вся логика под капотом весьма простого API.
почти гарантированно создаст проблемы при сопровождении
любой нетривиальный код, решающий нетривиальную задачу, вызовет проблемы при сопровождении, если разработчики не будут даже пытаться разобраться в коде. Напомню, что наша дискуссия началась с фразы «пишу библиотеку с кучей требований по перфу и памяти, приходится использовать всякие ухищрения».
UFO just landed and posted this here

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

UFO just landed and posted this here

Ну да, немного неправильную аналогию привел. Автор предлагает установить подушки безопасности ВО ВСЕ автомобили, включая гоночные болиды

Причем подушки безопасности конкретно одного производителя

UFO just landed and posted this here

Мне кажется, что популярность раста обеспечивается, в том числе, что у него порог входа относительно низкий. Да, язык совсем не простой, но сравнимый с С++, на котором пишет множество людей. В чём-то сложнее, в чём-то проще. Так вот если плюсы принято ругать за переусложнённость, то хаскаля в среднем боятся ещё больше. И это "всего лишь" хаскель.


Кстати, что есть мощное и безопасное без сборки мусора? Agda и ATS? Да, где-то применяются, но даже относительно раста этого не особо видно. Может, конечно, не там смотрю. Так-то я надеюсь, что индустрия будет двигаться в эту сторону, но пока это всё весьма далеко от мейнстрима.

UFO just landed and posted this here
Про ATS не знаю, а в агде точно сборка мусора есть.

Да, это я "немного" с адой перепутал. Но всё-таки: получается ничего особо и нет без сборки мусора?..

UFO just landed and posted this here
Кстати, что есть мощное и безопасное без сборки мусора? Agda и ATS?
Навскидку, Ada, BetterC, Swift, Zig.

Раст я списал в утиль. Металлолом, чё.
Ada

Бесполезна без SPARC. И да, какой там профиль Ravenscar выбирать?


BetterC

Который не является самостоятельным языком, а режимом компилятора D и на котором львиная доля библиотек не скомпилируется.


Swift

Который вставляет подсчёт ссылок для каждого класса? Не смешите меня.


Zig

Который базируется на принципе "программист знает, чё делает", так же, как и C.

Экий Вы максималист =)
Я ответил на конкретно заданный вопрос.

Кстати, есть статья, в которой анализируется надежность Zig в сравнении с Rust. Небольшое преимущество как по мне.

Да и в остальном я не очень то согласен. Можно критичные к надежности участки программ писать на безопасном подмножестве, а типичный бойлерплейт — на удобном языке.

Из перечисленного под критерий "мощное и безопасное" подходит только ада. Свифт — приятный язык, но в чём его мощь? Да и безусловный подсчёт ссылок действительно не всегда подходит для системного (а иначем зачем от сборки мусора отказываться?) языка.


Статью про Zig читал, по моему, там основной упор как раз на удобство. Разумеется, это тоже важно, но не в контексте про "мощность и безопасность". Перечитаю, может подзабыл что-то.


Ну и начинать проект на D или Zig я бы побоялся. С растом в этом плане тоже не всё так хорошо, но мы людей успешно находили.


Раст я списал в утиль. Металлолом, чё.

Объективно, ничего не скажешь.

Это другая статья про Zig, не та что я переводил.
Ну и начинать проект на D или Zig я бы побоялся.
Я бы тоже. Конкретнее, я даже С++11 побоялся в своей конторе — типичные люди не тянут, выбрал С#. Через 13лет видны конечно и недостатки такого выбора.

Объективно, ничего не скажешь.
Раст не годится для энтерпрайза — птичий, сложный и неудобный язык. Надеюсь, найдет свою нишу. Где-нибудь в драйверах.
Раст не годится для энтерпрайза — птичий, сложный и неудобный язык.

С "неудобный" я бы поспорил. Ни в одном мейнстримном ЯП нету сумм-типов. О каком удобстве вообще может идти речь?

Да и со сложный я бы поспорил. Относительно какого-нибудь c++11-20 довольно легкий язык.

Ни в одном мейнстримном ЯП нету сумм-типов.


variant & optional?
Это другая статья про Zig, не та что я переводил.

Да нет, мне именно статья по ссылке попадалась. Правда я её скорее пролистал, так что от перевода не отказался бы. (:


выбрал С#. Через 13лет видны конечно и недостатки такого выбора.

Было бы весьма любопытно послушать подробности.


Раст не годится для энтерпрайза — птичий, сложный и неудобный язык.

Что ж, могу порадоваться, что я не энтерпрайзом занимаюсь.

Лично я из языков, дающих сопоставимые с Rust гарантии, и не требующих рантайма, знаком только с ATS, и он не для людей.

небезопасный и слабый по сравнению с чем? С Idris? А с языками из условного top20 github?
Хорошо придумывать аналогии — они подобны котёнку с дверцей. Безопасных языков много, просто некоторые из них не снискали популярности, на некоторые нужно переучиваться. Кроме описанных в текущей статье, можно вспомнить как минимум этот список, и он ведь тоже не полный.
UFO just landed and posted this here

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

У встроенных проверок есть одна неприятная особенность. Они могут неудовлетворять имеющейся инфраструктуре. Например, есть метод at, который бросает исключение. Но какое исключение? Есть ли в этом исключении нормальное описание проблемы? Stacktrace? А проект все это требует

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

Когда я смотрю на исключение, выванное at, я понимаю, что лучше бы он упал с нормальным core dump-ом.


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

Не уверен, что аналогия удачная. Гонщик в гоночном болиде защищён заметно лучше, чем водитель в обычном автомобиле.

А нам предлагают еще и подушкой безопасности защитить

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


Нет. Речь идет именно об инструментарии для создании чего-то нового, а не о конечном потребительском продукте.

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

Да, ошибок use-after-free, переполнений буфера и тому подобного в коде на С не бывает. И в линуксе такого не было, и в openssl не было, и нигде не было.


Было. Но это было следствие невнимательности или низкой квалификации разработчика. И никто не скрывал что это возможно и что требуется думать о том, как ты распределяешь память и как с ней работаешь (в том числе). Т.е. от разработчика требовалась некоторая квалификация, а не просто прослушать пару бесплатных курсов на трубе.

А автор предлагает некий «язык для даунов» который поправит все ошибки, сам освободит память и т.п. Задача разработчика — выбрать правильный фреймворк и собрать из кубиков очередное поделие. А коль юзер начнет жаловаться что все тормозит, попенять ему что у того железо слабое.

Это и есть стиль «херак и в продакшен». Быстро и эффективно. А там трава не расти.
UFO just landed and posted this here
Точно не над openssl, точно не над ядром линукса

Как вы лихо подменили отрицание квантора всеобщности на всеобщность отрицания

UFO just landed and posted this here

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

UFO just landed and posted this here

Дык я вроде и не хвалю. C++ — ужасный язык

Ну так зачем хвалить С++, аппелируя к тому, как он хорош в руках программистов, которых не бывает?
странная логика. Крупнейшие современные софтовые проекты написаны на плюсах и на си (браузеры, компиляторы, виртуальные машины, ОСи...), и пара багов помножает на ноль квалификацию их авторов? То есть если я зайду в багтрекер какого-нибудь rust-проекта и увижу там некоторое количество тикетов, я тоже могу сказать что на нём невозможно разрабатывать, видите ли потому что баги бывают?
UFO just landed and posted this here
Не пара, а сильно больше
так вы оценивайте число багов относительно размера проекта. Так то понятно что в средней раст тулзе багов будет меньше, чем в хроме, но это потому, что в ней даже строчек столько не наберется.
и, вообще говоря, топовых специалистов в области.
вы уж определитесь, «топовых специалистов в области» или «хороших с++ программистов не бывает»? А то походу квалификация среднего программиста на с++ зависит только от приводимого в споре аргумента
Вопрос в том, без багов какого рода невозможно разрабатывать
опять — как плюсы, так невозможно без багов разрабатывать. А как раст, так никто никогда в unsafe не ошибается, или я не понимаю и это другое?
UFO just landed and posted this here
Одно другому не противоречит. Топовые программисты на C++ всё равно могут допускать (и допускают) ошибки в значимом количестве
а «в значительном количестве» это сколько? Вот например взять среднего питониста и среднего плюсовика, замерить errors/kLOC, и какие числа получатся? Просто если вы так уверены в достоверности своих тэзисов, наверняка у вас есть эксперименты, способные их подкрепить? В конце концов, должно же быть что-то объективнее чем «а вон в хроме по ошибке в пару дней», учитывая что там дифф/час измеряется тысячами строк.
UFO just landed and posted this here
А как раст, так никто никогда в unsafe не ошибается, или я не понимаю и это другое?

Вот только unsafe будет локализован и в нормальных проектах его стараются минимизировать. А в С++ (грубого говоря) — весь код unsafe.

UFO just landed and posted this here

Ну, если сможете показать как и чем это поможет, то будет круто. Вопрос то в простоте и массовости. И на c++ можно нормально писать, если знать как

UFO just landed and posted this here
Вы о каком-то конкретном примере?

Я о том как писать код собственно.


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

У меня тоже. О чем тогда спор?


Просто мало кто знает.

Это и называется опыт.


Ну и если эта самая внимательность у вас бесконечная.

Почему во всех утверждениях аппелируете к абсолютной внимательности? Никого она не волнует, а вас волнует

UFO just landed and posted this here
О том, что с некоторыми инструментами это всё же делать проще, чем с другими

Да, проще. Может быть. Чуть-чуть. Но не на порядок. Правильные "паттерны" (паттерны не в том смысле, как подумали) дают выигрыш сильно больше.


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

Да хоть комментарий можно добавить, я не знаю

UFO just landed and posted this here
Вот только если бы эти комментарии машина бы как-то проверяла и сама ругалась, если они либо устаревают, либо вы им не следуете…

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

Вы столкнулись с «парадоксом Блаба».
for(var i = 0; i < a.length; i++) {
  f(a, i);
}

function f(a, i) {
  a[i] = 0;
}
Без зависимых типов, нам нужно либо в теле функции f проверять выход за пределы массива руками, либо во время выполнения будет ошибка, если кто-то передаст неверный индекс. Зависимые типы позволяют убрать проверки, доказав, что индекс находится в нужных пределах.
Но в данном примере что даст проверка на верный индекс при вызове функции? Проверим что индекс верный на входе в функцию. А в теле у нас такой код будет

function f(a, i) {
  a[i+1] = 0;
}

И второе, нам же надо либо верифицировать всю программу и тогда она будет написана примерно никогда, либо верифицировать только критически важные моменты, но так как это человек решает что критически важно а что нет, то он снова ошибается. А даже если не ошибается и верно опеределил все критически важные моменты, то 90% работы он уже сделал, надо более внимательно их обработать. Разве нет?
UFO just landed and posted this here
Но в данном примере что даст проверка на верный индекс при вызове функции?
Нет. Проверяется индексация и она поднимается по стеку вызова. Каждое действие с i всего лишь смещает диапазон.

То есть в моём примере это -1 < i < a.length, в вашем -2 < i < a.length - 1
И второе, нам же надо либо верифицировать всю программу и тогда она будет написана примерно никогда, либо верифицировать только критически важные моменты
В идеале, абсолютно внимательный программист на си и так не забывает ставить эти проверки. Проверять придётся далеко не каждую строку, например, мой пример выше соберётся без изменений, просто потому, что компилятор может доказать его самостоятельно. Если же речь идёт об уже написанном коде, то никто не мешает внедрять проверку постепенно, шаг за шагом.

А можно так
for(var i = 0; i < a.length; i++) {
f(a[i], i);
}
Тогда никакие проверки в f не нужны вообще

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

Тогда нужен более конкретный пример. Не вижу, что еще обсуждать в данном примере

Зависимые типы гарантируют, что как код внутри f, так и вызывающий f не будут приводить к выходу за границы массива.

В примере выше я обошолся без завтипов. Несите другой

Вы вынесли индексацию за пределы функции. Если у вас структура сложнее массива или обращение будет по десятку индексов, вы тоже будете выносить этот код выше?

Нужно смотреть конкретный пример, а не гадать почем зря

Держите несколько примеров. Только не говорите про тесты, так как вам придётся тестировать вызывающий код, а он ещё не написан.

Ну посмотрел, не нашел ничего, что нельзя было бы покрыть обычными типами с проверкой в конструкторе. Да и тесты никто не отменял, чтоже в них плохого?
А вот вы скажите мне одну вещь. В этой статье рассматривается пример деления на 0. И для этого вводится специальный тип, который заставляет проверять, является ли делитель нулем. Но (не знаю, так ли в haskell) исключительной также является ситуация деления maxint на -1. Где это учитывается? Автор об этом забыл?

Ну посмотрел, не нашел ничего, что нельзя было бы покрыть обычными типами с проверкой в конструкторе.
Надёжность получается как у интерпретируемых языков — до тех пор, пока данная ветвь кода не выполниться, о существовании ошибки знать никто не будет.
Да и тесты никто не отменял, чтоже в них плохого?
Вы будете писать тесты каждому, кто скачает вашу библиотеку? Если вы пишите код в команде, то вы непрерывно отслеживаете кто и с какими аргументами вызывает ваши функции?
Но (не знаю, так ли в haskell) исключительной также является ситуация деления maxint на -1. Где это учитывается?
ghci
GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
Prelude> :t 5 / 7
5 / 7 :: Fractional a => a
Prelude> 5 / 7
0.7142857142857143
Prelude>

Я не понял, а если деление вещественное, разве на него действуют правила деления на 0?

UFO just landed and posted this here
UFO just landed and posted this here
а попробуйте удалить доказательство в какой-нибудь функции посередине

Но это в том числе усложняет прототипирование, когда нужно 2 дня разбираться в иерархии классов вместо написания кода.


Что за их полнотой, корректностью и актуальностью никто не следит

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

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

А для прототипирования есть специальные методы, которые позволяют обходить проверки, например unwrap для option, если что-то пойдёт не так, то программа безопасно упадёт.

Не понял, при чем тут долго собирается.


И как потом убирать все unwrap из кода? Плюс хорошо, когда они есть.

И как потом убирать все unwrap из кода?
Как обычно убирают костыли — переписать небольшие фрагменты. В случае rust, это будет либо match, либо if let. Возможно потребуется дописать какой-то код, вроде сообщения пользователю, что он сделал что-то не то, например ввёл строку вместо числа.

Это понятно. Я имею в виду как найти все такие места. И что делать с типами у которых unwrap метода нет

Есть инструментарий, который помогает поймать все вызовы panic!. Все методы типа unwrap и прочие, которые просто прерываю выполнение программы в конечном итоге вызывают panic!.

Но смысл то не в том, чтобы дождаться паники, а в том, чтобы избавиться от всех unwrap

Неясно выразился, этот инструмент "ловит" во время компиляции. Превращает наличие вызова panic! в ошибку компиляции. Грубо говоря проверяет наличие "call stdlib_panic" в коде пользователя.

С помощью соответствующего анализатора. Вот конкретно для rust.

Метод типа unwrap нужен для очень ограниченного подмножества типов. То есть при преобразовании строки в число у вас будет точно такой же Option, как и в куче других случаев.

Если взять другой язык, например Crystal, то там будет ещё проще.
a = some_condition ? nil : 3
if a.nil?
  # here a is Nil
else
  # here a is Int32
end

Анализаторы и в c++ можно использовать

Мне кажется, или мы идём на второй круг? В случае rust или какого-то другого безопасного языка анализатор нужен только для того, чтобы убедиться, что код не падает, так как кто-то забыл проверить какое-то условие. В случае плюсов анализатор нужен для уменьшения количества очевидной порчи памяти, не очевидную порчу он не распознает.

Но всеже анализатор нужен.
Как видим, для Того, чтобы писать надежный код, требуются +- одни практики, вне зависимости от того, rust это, c++ или haskell

Анализатор нужен, либо если вы берёте чужой проект сомнительного качества, либо сами написали кучу кода на костылях впопыхах.
UFO just landed and posted this here

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

Я бы решил его, обмазавшись проверками по максимому. Проверка, что результирующая строка не пустая, проверка, что файл открылся, еще к-нибудь. Как решть эоо завтипами?
Требуется доказательство наличия третьего элемента в массиве, а поскольку доказательства нет, то код не пройдёт проверку.

Для ваших проверок без машины с виндой заподозрить что-то неладное не получиться.

Ну и как бы это выглядело хотябы в псевдокоде?

Ориентировочно как-то так
{-@ type PathList = {p : List | length p > 2} @-}
Так мы объявляем тип для parts. Потом, после этого явно определяем тип выражения
getUserName :: Home -> Text
getUserName homeDir = parts !! 2
  where
    parts :: [Text] -> PathList
    parts = splitOn "/" homeDir
После этого, можно запускать проверку типов.

Ну я не увидел что хотел. Думал увидеть как это используется. Не получится ли так, что ошибка "скроется" типами и под виндой также проскочит?

Нет, ошибка наоборот проявится до запуска кода — либо при компиляции, либо при вызове проверки, в зависимости от того, что её реализует. В случае liquid haskell — это отдельный инструмент, который вызывается до компиляции.

Ну вот я не понимаю. Вот был код под линукс, который както вызывал эту функцию. Все работает, не падает. Потом переносим этот код под винду. Почему он вдруг упадет в compile time?

UFO just landed and posted this here

К сожалению я не понял, что от меня требуется. Гарантировать безопасность cnt>=64? Если сильно упороться, то в oop языках можно создать целый класс cnt, который принимает по ссылке вектор и имеет метод с выразительным именем, который проверяет, что не превысили размер вектора

А извиняюсь, с мобильника не удобно. Но проблем все равно не вижу. Проверки, тесты, что-то можно повыносить в классы

UFO just landed and posted this here
Это кусок jpeg-декодера, здесь лишний if на каждую операцию записи — это дорого.

Обычно я не разговариваю о производительности до предоставления замеров. И да, я писал кодер jpeg-а, и там было полно проверок

UFO just landed and posted this here

Тем не менее все перечисленные мной способы работают. А как и по какому принципу работают завтипы, я пока не видел

UFO just landed and posted this here

Может потому, что их там не используют? Как и завтипы впрочем

UFO just landed and posted this here

А о каком проекте вообще речь?

UFO just landed and posted this here

А можно через завтипы что-нибудь сделать с типом DateTime? Ну например чтобы компилятор нам как-то мешал искать интервал между датой в московском часовом поясе и другой датой по Гринвичу.

UFO just landed and posted this here

Не, речь не о присутствии/отсутствии таймзоны.
Вопрос в том, чтобы не путать между собой даты в разных таймзонах.

UFO just landed and posted this here

Я правильно понимаю, что выражение
parseWithTZ : String -> Either ParseError (tz ** DateTime tz)
надо читать как


сигнатура функции, которая
  1. называется parseWithTZ
  2. принимает String
  3. возвращает Either, который содержит сообщение об ошибке парсинга или кортеж значений
    кортеж содержит дату и тип

?


в условной Jawa подобная функция бы выкидывала условный FormatException и возвращала бы условный DateTimeWithTimeZone


Как тогда получается написать
case decEq tz1 tz2 of
если tz1 потенциально может вообще не быть?


Кажется тут надо более развесистый pattern-matching — предусмотреть ситуации когда не получилось преобразовать первую, вторую или обе сразу.

UFO just landed and posted this here
А можно через завтипы что-нибудь сделать с типом DateTime?

Разве для этого нужны зависимые типы? Если у нас DateTime параметризируется тайм зоной, то этого достаточно?

UFO just landed and posted this here
Ну расскажу я, как сделать формально верифицированное красно-чёрное дерево или сортировку, неужто это будет полезно в контексте этой дискуссии?

Это будет полезно в контексте хабра. Лично я очень хотел бы видеть подобные статьи на данном сайте и был бы благодарен за такую статью.

И что новое вы создаёте ножом и вилкой?


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

Так устроит?

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

Где же эти разработчики с высокой квалификацией, над чем они работают?


Не знаю. Видимо, это те, кто создавали такие вещи как AS/400 (написана на плюсах), AIX, QNX (согласитесь — это достаточно серьезные коммерческие продукты).

Точно не над openssl, точно не над ядром линукса


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

Ну бывает, ну шутя
Сбилась с верного путя
Так ведь я — дитя природы,
Пусть дурное, но дитя
(с) Л.Филатов


Не люблю такую аргументацию, но позволю себе. Так вот, примерно за 17 лет писания кода на плюсах, из которых лет 15 я получал за это деньги, из которых лет 10 — фуллтайм, я убедился, что на плюсах (и на С) писать безопасный и надёжный код в программах сложнее хелловорлда за адекватное время невозможно.


Странно, но у меня почему-то получалось и получается.
На С начал писать в 88-м (Turbo C 1.0). С 91-го — фуллтайм за деньги. Когда перешел на плюсы не вспомню уже, помню что это был (что удалось достать) Turbo C++ 2.0
С 17-го C/C++ скорее вспомогательный язык т.к. основной — RPG (на платформе IBM i). Но часть вещей, требующих более тесного взаимодействия с системой, пишутся на С/С++.
Просто нужно понимать где и чем хорош С, и где он плох.

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


Ну если для Вас в С++ главное шаблоны…
Я вот достаточно редко использую все то, что наворотили в современном С++ И считаю это скорее злом чем добром поскольку оно скрывает в себе очень много кода и, главное, работы с объектами — разработчик перестает контролировать объекты, которые выделяются без его ведома и живут свое жизнью (а дальше начинаются непонятные утечки памяти, страдания по сборщику мусора и т.п.). Поскольку прелесть С в его прямой, фактически, работе с памятью и достаточно свободном обращении с типами. Для, для неопытного разработчика это однозначно зло.

Дай ребенку скальпель порежется. Дай скальпель хирургу — удалит опухоль.
UFO just landed and posted this here
это я тоже дурак и использую технику для дураков?

Ну учитывая недавний случай, когда двое граждан как-то умудрились обмануть так называемый «автопилот» теслы и заставить его ехать, когда на сиденье водителя вообще никого не было… поневоле задумаешься на тему того, на какую аудиторию это все рассчитано. Перефразируя известное высказывание Мольтке про приказы, «любое устройство, которое может быть использовано неправильно, обязательно будет использовано неправильно».
blind spot monitor, brake assist, радар с автообнаружением препятствий

Это всё примерно -Wall. А автопилот автомобильный — то вообще рандом, никакими доказательствами и детерминизмом там не пахнет.

Видимо, это те, кто создавали такие вещи как AS/400 (написана на плюсах), AIX, QNX (согласитесь — это достаточно серьезные коммерческие продукты).
image

Сколько там уязвимостей в ms-dos? Всем срочно переходить на ассемблер.
ms-dos написана на ассемблере
Полагаю, что в сумме, в ms-dos всё же меньше уязвимостей, чем в linux.
Разумеется, причины:
  1. гораздо меньшая кодовая база
  2. профессиональные разработчики
  3. отсутствие сетевых функций

Но вирусов под нее, использующих уязвимости, было предостаточно. Даже я один написал =)
Я не уверен, что можно как-то сравнивать, потому что i8086/8 принципиально аппаратно уязвим.
Не люблю такую аргументацию, но позволю себе. Так вот, примерно за 17 лет писания кода на плюсах, из которых лет 15 я получал за это деньги, из которых лет 10 — фуллтайм...
У вас из года в год одна и та же ошибка в логике. Вы критикуете с++ основываясь на опыте с++98/03, однако совершенно не откалибровавшись на современном с++ продвигаете раст, вышедший позже с++14
UFO just landed and posted this here
Да, новые стандарты сильно упрощают некоторые вещи и позволяют писать более выразительный и даже безопасный код
о, вы наконец-то согласились с моей аргументацией… Сколько там лет прошло?
Нет, новые стандарты не способны сделать из C++ идрис или хотя бы раст.
а этого я и не утверждал. Мой поинт немного в другом — предположим раньше я бы допустил 10 ошибок памяти и 3 логических, то теперь это будет 1 ошибка памяти и 3 логических, и теперь логические беспокоят меня куда больше.
UFO just landed and posted this here
Хотя, учитывая, что я достаточно часто видел баги, связанные с использованием объекта, из которого мувнули (std::string с SSO передаёт привет), или захват в лямбду ссылки на локальную переменную, или ещё какую подобную ерунду — на самом деле не факт, что это число не выросло
эти ошибки обычно локальные и не очень тяжело отслеживаются. По краней мере найти захват по ссылке в лямбде куда проще, чем прыгать по проекту перепроверяя сырые указатели на null, а также лайфтаймы, владение и const-correctness объектов за ними.
UFO just landed and posted this here
сторонники C предлагают некий «язык для даунов», который сам аллоцирует регистры, сам настроит стекфрейм


Ну так это и есть приемлемый даунический уровень. Выше идти не всегда полезно.

Раз тут срач, повторю свою типичную мысль последних лет: миру нужны только С для лоу-левела, Сишарп для энтерпрайза и Хаскель для умных. Остальное запретить, плюсоводов ввиду большого к ним уважения — наградить именными часами.
Автор предлагает запретить использование ножей и вилок потому что ими можно порезаться/уколоться?

Нет, автор предлагает заменить опасную бритву безопасной.

UFO just landed and posted this here
UFO just landed and posted this here
А надо не варнинг кидать, а запрещать такое компилировать, как делает Раст.

Боюсь опустить карму ещё ниже, но побуду адвокатом дьявола в среде ратующих за Раст/Свифт.
Замечу, что использую раст как замену легаси плюсовому коду, так что устриц пробовал/пробую регулярно.


/Wall и ему подобные помогут благородному дону в борьбе с подобным.
В прикладном коде — отличный рецепт борььбы с подавляющим большинством ошибок уровня «мусор в optional». Практически, что проскочит компилятор взрослые дяди ловят ассертами на дебажных билдах.
Ну, а то, что уже и после юнит/автоматизированого/ручного тестирования (вы же пишете тесты, правда?) таки всплывет — так и Rust со Swift небезгрешны, плюс далеко не везде получится даже собрать нормально бинари плюсовые с частями на расте(да-да, легаси так быстро не умирает и кое-где даже шлангом собрать не очень-то и просто), а переписать все сразу — денег нет.

Rust запрещает присвоение Option<T> в T, ровно так, как это происходит в C++. Разница в том, что Option::unwrap — паникует в случае None, а его аналог из C++ — разыменование — нет.

В с++ есть optional::value() которая бросает исключение по аналогии с unwrap(). Главный плюс раста, что есть синтаксический сахар вида if let/match/?/.map() такого типа фичи и проверки от компилятора есть в том же тайпскрипте.

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


Наверное, можно статическим и анализатором находить и запрещать operator* для option

Непонятно только, почему operator* не делает того же самого. Кидал бы исключение. Если программист перед вызовом operator* корректно проверит значение сам, то оптимизатор вырежет лишнюю проверку, и потерь производительности не будет.

Видимо исторически так сложилось начиная с Си и заканчивая всей стд, что когда делаешь *x или x-> то это самая базовая операция без всяких проверок.

Никакого "исторически сложилось" здесь нет. Это осознанное решение.


Нужны проверки (медленнее и безопаснее) — пользуйтесь std::optional::value или std::optional::value_or.
Не нужны проверки (быстрее и опаснее) — пользуйтесь std::optional::operator->, std::optional::operator*.


Интерфейс не ограничивает пользователя в возможностях.

Почему не наоборот? Value — без проверки оператор звёздочка — с ?

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

это я и подразумевал под "исторически сложилось".
Кароче не важно, это разговор ни о чём :)

вы путаете «исторически сложилось» и логику языка.
int *p = get_data();
if ( p )
    ( *p ) = 10;
и тут следующий код обсолютно повторяет эту логику но с другим типом данных
std::optional< int > opt = get_data();
if ( opt )
    ( *opt ) = 10;
более того, это может быть обёрнуто в шаблон
template< typename _T >
int foo() {
    _T p = get_data();
    if ( p ) {
        ( *p ) = 10;
    ...
и, опять же, поведение кода будет одинаково и для указателя на int и для std::optional

в предлогаемом же вами варианте с «наоборот» поведение будет различным, а для его сохранения придётся писать специализацию шаблона, который будет вызывать ::value()
А программисту каждый раз нужно будет помнить где какой оператор вызывать, звёздочку или value(), при том, что value() сущестует только для std::optional что приведёт к куда бОльшим ошибкам по итогу.

Затем, чтобы операцией с проверкой было удобнее пользоваться, а перед использованием небезопасной программист думал, а точно ли эта операция безопасна.

Я вообще за несколько лет так и не разобрался, как пользоваться string_view. Вот принимаешь ты в параметре функции string_view, тащишь его через 10 своих методов, а в конце какой-нибудь библиотечный метод, который принимает string, и все, нужно конвертировать в string, тратить аллокацию памяти. Вот как это использовать?

UFO just landed and posted this here

Интересно, что-то сильно мешает разработчикам плюсовых стандартов/компиляторов помечать какие-то устаревшие и или небезопасные возможности как deprecated и просто не давать их использовать без явного разрешения (для совместимости с легаси). Внедрить в новый стандарт С++ ownership как в расте и const по умолчанию, при сохранении всего остального как было, разве плохо. А то раст местами конечно хорош, а остальное всё сломали "до основанья а затем", приходится вообще по-другому писать.

https://isocpp.github.io/CppCoreGuidelines/


The rules are designed to be supported by an analysis tool. Violations of rules will be flagged with references (or links) to the relevant rule. We do not expect you to memorize all the rules before trying to write code.

The rules are meant for gradual introduction into a code base. We plan to build tools for that and hope others will too.
Используем старый добрый с++ без наворотов и будет нам счастье. Конечно надо читать особенно то что в спецификации описано как «поведение неопределённо»
Вот, ктсати, так и стараюсь делать. То, что принято называть «С с классами». Но я к острым ножикам привычный, начинал с С + asm а это, все-таки, мозги формирует.
UFO just landed and posted this here

что-то мне подсказывает что использование ссылки на shared_ptr не то чтобы тот кейс, для которого того создавался и вроде как явно иллюстрирует неправильную "готовку"

UFO just landed and posted this here
ссылка на shared_ptr — это норм лишь в паре конкретных кейсов, зачем тогда вообще юзать shared_ptr? если в серьез стоит вопрос об экономии декремента рефкаунта, тогда уже берем ссылку на unique_ptr с тем же успехом, но суть в том, что ни тот ни другой изначально сделаны не для этого

во-первых, в unique_ptr нет рефкаунтинга. Во-вторых, его можно передать по ссылке если хочется передать объект в функцию не передавая владение на него. В-третьих, shared_ptr придуман чтобы упростить управление лайфтаймом объектов, а не чтобы на ровном месте замедлить код дерганием атомиков

В таких случаях надо shared_ptr захватывать через std::move, кстати. Тогда и безопасно будет, и накладных расходов на рефкаунтер нет.

Это если функция принимает владение shared_ptr. А если по факту одалживает, то будет атомарный инкремент + атомарный декремент на пустом месте.

Тогда тем более не нужен shared_ptr, потому что тут
захватывать через std::move, кстати. Тогда и безопасно будет, и накладных расходов на рефкаунтер нет
описано поведение unique_ptr
В таких случаях надо shared_ptr захватывать через std::move, кстати. Тогда и безопасно будет, и накладных расходов на рефкаунтер нет.
вот у вас лежит в объекте shared_ptr, и его нельзя инвалидировать, он там нужен. Надо передать его в функцию, принимающую rvalue. В итоге вам придется сделать копию, и дальше мувнуть её в метод, который эту копию потом уничтожит. Вот вам и накладные расходы на рефкаунтинг.
UFO just landed and posted this here
А, ещё иногда функции нужно начинать разделять владение не всегда, и тогда передавать всегда по значению может быть пессимизацией, но это уже более редкий кейс (но тоже возможный, да).

Что-то я с трудом представляю себе подобный случай. Можете привести пример?

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

Современный С++ позволяет писать надёжный и безопасный код. Если ты этим не пользуешься, то не нужно критиковать язык. Используй надёжные механизмы и не будет проблем.
Воспитательница в детском саду спрашивает: «Дети, в какой стране самые красивые игрушки?» Дети (хором): «В Советском Союзе!» Воспитательница «А в какой стране самые нарядные детские одежды?» Дети (хором): «В Советском Союзе!» Воспитательница: «А в какой стране самое счастливое детство?» Дети (хором): «В Советском Союзе!» Вдруг Вовочка заревел. Воспитательница: «Вовочка, почему ты плачешь?» Вовочка (сквозь слезы): «Хочу жить в Советском Союзе!»
UFO just landed and posted this here

Примеры, конечно такие себе. Можно и пример кода с разыменовыванием нулевого указателя привести. Тот, кто пишет на С++ так точно писать не может. Да и вообще любой мало-мальски новый компилятор тут ворнинг даст. А если еще статический анализатор подтянуть.

Опять пиар очередной замены C++? С++ не зря прожил столько десятилетий и многих пережил. Очередная «замена» решает только некоторые проблемы плюсов. Но когда дело дойдет дело до действительно сложных задач, то окажется, что много в новом языке не хватает — будут например добавляться убогие генерики вместо полноценных шаблонов и т.д. В конечном счете многолетний опыт C++ скорее всего победит. Думаю было бы гораздо лучше, если бы средства тратились на развитие существующего языка и компиляторов, чем начинать все сначала… ИМХО
UFO just landed and posted this here
будут например добавляться убогие генерики вместо полноценных шаблонов

Ну у C++ есть ряд преимуществ, например наличие кучи готового кода на C++, почти бесшовная интеграция с C, наличие разных компиляторов (что также является недостатком). Но шаблоны, серьезно?


Не знаю какие языки вы имели ввиду по "заменой C++", но например в Rust можно писать плагины к компилятору (известные как "Procedural Macros") для того что не влезает в "generic" и обычные "macro_rules!". И тут C++ явно проигрывает, все-таки программировать на обычном языке программирования и на функциональном языке (а-ля "шаблонах c++") в котором и отладочную печать хрен реализуешь это совершенно разные вещи.


А часть того что делают "Procedural Macros" вообще реализовать нельзя в рамках текущего стандарта, я имею reflection, который вроде в c++23 будет или позже.

Макросы ортогональны шаблонам а те в свою очередь дженерикам. То что можно сделать на макросах в расте точно так же достигается каким-то сторонним кодогеном на основе libclang. Или можно вспомнить Qt moc которому уже очень много лет. На расте оно сделано все более удобно с точки зрения туллинга, для небольших и средних проектов это плюс. То что можно сделать на шаблонах в общем случае нельзя сделать ни на макросах ни на дженериках.

о что можно сделать на шаблонах в общем случае нельзя сделать ни на макросах ни на дженериках.

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

А можно пример, того что нельзя сделать с помощью плагина к компилятору, но можно сделать на шаблонах?
отвязать язык от компилятора?
compile-time трансляция числа в строку, опять же compile-time заполнение таблицы, ну хотя бы синусов/косинусов, для примера.

В шаблонах есть оверлоадинг в зависимости от типа аргумента и других его свойств. Сам шаблон может быть передан аргументом тоже.

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

Шаблоны это и есть другой язык с другими парадигмами программирования и другими навыками для его освоения.


Не вижу, в чем будет проблема в интеграции "обычного раста" и плагинов компилятора. Плагин просто генерирует код, совершенно такой же, какой вы бы сами написали. Никакой магии он не делает

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

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


И потом, еще можно поспорить, что там лучше поддерживается. Насколько у шаблонов C++ все хорошо с совместимостью? А то поддерживается там может только на бумаге. Или только в новейшей версии компилятора. Сколько тут людей плакалось, что на проекте древний как гавно мамонта GCC и обновить его нельзя?

Проблемы с версиями испокон веков решаются менеджерами зависимостей. Не быть его не может
Сурприз-сурприз.
Насколько у шаблонов C++ все хорошо с совместимостью?
Для этого есть стандарт языка и щаблоны часть его. А стандарт для плагина можно посмотреть?
На днях как раз была статья из серии «оно не логично, но оно так на всех компиляторах, потому что поведение описано в стандарте», что-то там про знаковые и беззнаковые вычисления.
Сколько тут людей плакалось, что на проекте древний как гавно мамонта GCC и обновить его нельзя?
Люди хотят новый фичей, которых в старом компиляторе нет, но при этом они могут собрать проект любой версией новее. Если писать на основе стандарта С++98/03 то можно будет собрать вообще любым компилятором.
Сурприз-сурприз.

Сомневаетесь, что менеджер зависимостей скачает зависимость или что?


Для этого есть стандарт языка и щаблоны часть его.

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


А стандарт для плагина можно посмотреть?

Можно. Открываете грамматику языка и вперед.


Люди хотят новый фичей, которых в старом компиляторе нет, но при этом они могут собрать проект любой версией новее.

Почему вы решили, что хотят именно новых фич? Может у них там встраиваемая система какая-то, компилятор для которой какой дали, тем и пользуйся, а другие просто не умеют под нее компилировать.

Сомневаетесь, что менеджер зависимостей скачает зависимость или что?
Что он используется в случае с С++, и опять же — который из множества, даже CMake умеет в FetchContent из git, а это ни разу не пакетный менеджер :)?
И как долго используемый в проекте компилятор придерживается современного же этой версии компилятора стандарта?
Не понял. В целом — всегда. Не меняешь компилятор — не меняется ни чего в языке и стандарте, который он поддерживает. Нужен С++20, то да, GCC 4.8.5 не подходит, нужно обновить до 11-й версии, ну а новой версии можно указать "-std=c++11" и GCC 11 «магическим» образом превращается в тыкву, в смысле работает только с поддержкой стандарта С++11 и ни чего новее не понимает.
Можно. Открываете грамматику языка и вперед.
Ну вот то есть, если я в системе сборки на основе CMake вызываю, скажем, configure_file(foo.h.in foo.h ONLY) и мне на основе шаблонного файла foo.h.in будет сгенерирован foo.h это имеет какое-то отношение к синтаксису языка? И мне не нужно изучать что такое #cmakedefine, внезапно, не описанный в стандарте языка С++?
Может у них там встраиваемая система какая-то, компилятор для которой какой дали, тем и пользуйся, а другие просто не умеют под нее компилировать.
Именно, потому что почти все компиляторы для встройки поддерживают С++98/03 и редко когда умеют в С++11/14 не говоря про С++17
Проблемы могут быть только если в компиляторе покопались шаловливые ручки «тех кто дал» для поддержки специфичных функций.
Что он используется в случае с С++

Мы же обсуждали, каким образом плагина компилятора вдруг не окажется в проекте, где он нужен. В каком из компиляторов C++ есть плагины?


Rust проекты используют менеджер зависимостей и там такой проблемы не может быть.


даже CMake умеет в FetchContent из git

Ну вот, а вы говорите, откуда возьмется плагин правильной версии. Оттуда же, откуда библиотека правильной версии.


Не понял. В целом — всегда.

Я имею ввиду не "в версии A.B.C перестаем поддерживать стандарт версии X", а "Все ли из того, что написано в стандарте версии X, реализовано в компиляторе версии A.B.C, или на словах у нас стандарт X, а не деле стандарт A (где A меньше X)?"


Ну вот то есть, если я в системе сборки на основе CMake вызываю, скажем, configure_file(foo.h.in foo.h ONLY) и мне на основе шаблонного файла foo.h.in будет сгенерирован foo.h это имеет какое-то отношение к синтаксису языка? И мне не нужно изучать что такое #cmakedefine, внезапно, не описанный в стандарте языка С++?

Для того, чтобы понять, что написано в сгенерированном foo.h стандарта достаточно. А уж откуда этот файл появляется — хоть заменой каких-то #cmakedefine в foo.h.in, хоть генерируется еще из какого-то другого места — значения не имеет. У вас есть "плагин" CMake, который выдает код, который интегрируется с любым другим кодом на C++ в той же мере, в какой код на Rust, выдаваемый плагинами к компилятору Rust-а, интегрируется с другим кодом на Rust.


Проблемы могут быть только если в компиляторе покопались шаловливые ручки «тех кто дал» для поддержки специфичных функций.

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


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

Мы же обсуждали, каким образом плагина компилятора вдруг не окажется в проекте, где он нужен.
Это не часть языка. Ну и если плагины скачиваются из git, то даже указывая версию 1.10.5 не факт, что получишь именно её, так как rebase и прочие трюки ещё ни кто не отменял.
Все ли из того, что написано в стандарте версии X, реализовано в компиляторе версии A.B.C, или на словах у нас стандарт X, а не деле стандарт A (где A меньше X)?
gcc.gnu.org/projects/cxx-status.html
clang.llvm.org/cxx_status.html
docs.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance?view=msvc-160
Но вероятно генерация кода под какой-нибудь специфичный чип невозможна без копания шаловливыми ручками. Что предлагает C++, если мы все же хотим пользоваться современными шаблонами? Не пользоваться ими.
Боюсь спросить как в таком случае будут обстоять дела с Rust? Не использовать его вообще?
В случае С++, как я и сказал, вариант стандарта С++03 будет работать всегда, не важно под какой чип.
Это не часть языка.

Интерфейс к вызову плагинов — часть языка. Плагины естественно не часть языка, иначе это были бы не плагины.


Ну и если плагины скачиваются из git, то даже указывая версию 1.10.5 не факт, что получишь именно её, так как rebase и прочие трюки ещё ни кто не отменял.

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




gcc.gnu.org/projects/cxx-status.html


GCC has experimental support for the latest revision of the C++ standard, which was published in 2020.

clang.llvm.org/cxx_status.html


C++20 -std=c++20 Partial

https://docs.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance?view=msvc-160
Тут сложно сказать, в каком состоянии поддержка последнего стандарта, информации много и она размазана, но я вижу с десяток No в разделе C++20.


И это самые новые релизы компилятора, стандарт год назад, как вышел, а обсуждался так вообще не меньше 3-х лет. А по факту выходит, что не хочешь внезапного возникновения проблем — жди еще года 3, пока поддержку наконец сделают полностью.
Боюсь спросить как в таком случае будут обстоять дела с Rust? Не использовать его вообще?
В случае С++, как я и сказал, вариант стандарта С++03 будет работать всегда, не важно под какой чип.

По вашей логике выходит, что любой компилятор C++ компилирует полю любой чип в режиме С++03; clang в том числе; clang генерирует код с помощью LLVM; Rust тоже генерирует код с помощью LLVM; значит с помощью Rust тоже можно написать код под этот чип.

Тут сложно сказать, в каком состоянии поддержка последнего стандарта
Эммм… ниже таблицы со всеми изменениями, со ссылками на каждый документ-изменение.
И это самые новые релизы компилятора, стандарт год назад, как вышел, а обсуждался так вообще не меньше 3-х лет.
Это вы так читаете или пытаетесь натянуть сову на глобус, подогнать реальность под своё мнение?
Финальная спецификация С++20 вышла в декабре 2020, ну то есть меньше полугода назад.
GCC
C++20 features are available since GCC 8

Clang
Default member initializers for bit-fields P0683R1 Clang 6

На то, что в Clang уже заявлена поддержка некоторых возможностей С++23 вы просто закрыли глаза :)
MSVC
P0704R1 Fixing const lvalue ref-qualified pointers to members VS 2015

В основном поддержки нет у тех вещей, которые и не были стандартихованы до декабря, те же модули, например.
clang в том числе
вы, видимо, не пишите под встройку, там правит бал GCC. Мне вообще интересно наблюдать, как вы обсуждаете область, где знания близки к теоритическим :)
Эммм… ниже таблицы со всеми изменениями, со ссылками на каждый документ-изменение.

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


C++20 features are available since GCC 8

Вот прям все-все? И багов нигде нет? Да и еще через полгода после финального релиза стандарта (вы там определитесь уже, или полгода стандарт как вышел, что ажно глобус с совой портятся, или 3 года как уже все готово (https://gcc.gnu.org/releases.html говорит, что 8.1 вышел 2 мая 2018))? Не верю.


На то, что в Clang уже заявлена поддержка некоторых возможностей С++23 вы просто закрыли глаза :)

Вот я выделил ключевое слово во всех этих восторженных отзывах. Стандарт как бы на то и стандарт, чтобы быть реализованным в полном объеме, а не "некоторыми возможностями". Полагаю, цитата


Default member initializers for bit-fields P0683R1 Clang 6

как раз и говорит об одной из "некоторых возможностей" стандарта C++20, и должна показать, что типа стандарт давно уже реализован. Может там даже что-то из C++32 уже есть, мм :)?


В основном поддержки нет у тех вещей, которые и не были стандартихованы до декабря, те же модули, например.

А… ну да… чуть-чуть беременна.


вы, видимо, не пишите под встройку, там правит бал GCC. Мне вообще интересно наблюдать, как вы обсуждаете область, где знания близки к теоритическим :)

Неа, не пишу. Просто интерпретирую мысли, которые тут вокруг роятся. Если они неправильные… что ж… как говорится, gargabe in — garbage out :).

Высчитывать по диффам поддерживаемые возможности на конкретную дату. Да ну нафиг, я не git.
А ви, таки, хотите всё и сразу или конкретную функциональность?
вы там определитесь уже, или полгода стандарт как вышел, что ажно глобус с совой портятся, или 3 года как уже все готово
Ви, таки, прочитайте как формируется стандарт и почему одни части документа были готовы ещё три года назад, а некоторые окончательно смогли оформить только через пару лет. И почему по документам, которые уже не изменятся, начинают реализовывать функциональность компилятора. Много полезного узнаете. Кстати, это покажет, кем вы себя демонстрируете перед обществом.
Просто интерпретирую мысли, которые тут вокруг роятся.
Можно тогда я интерпретирую мысль, что у Rust крайне токсичное сообщество и ему не место в приличном обществе, на основе, таки, вашего поведения, или как ви сказали
gargabe in — garbage out
А ви, таки, хотите всё и сразу или конкретную функциональность?

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


Много полезного узнаете.

Например, каким образом GCC, вышедший в 2018, поддерживает стандарт, вышедший в 2020 (заметьте, обе даты по вашей информации, а не моей)? Это действительно было бы интересно. Напомню, что этой фразой вы отвечали на вопрос


Все ли из того, что написано в стандарте версии X, реализовано в компиляторе версии A.B.C, или на словах у нас стандарт X, а не деле стандарт A (где A меньше X)?



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

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


Можно тогда я интерпретирую мысль,

Можно конечно, ведь, как уже неоднократно упоминали


gargabe in — garbage out

так что не так уж важно, что вы там наинтерпретируете :)

Ну то есть начали с того...
У стандарта периодичность выхода — раз в три года. А у компилятора? Это как раз было к тому, что поддержка вводится постепенно, параллельно с формированием стандарта. Зафиксированная и неизменная функциональность реализуется сразу после публикации, раньше чем выходит финальное издание стандарта, таким образом лаг между публикацией и поддержкой составляет несколько месяцев, а не три года. Я же говорю — сперва почитайте, а потом обсуждайте, что бы не выглядеть глупо.
Например, каким образом GCC, вышедший в 2018, поддерживает стандарт, вышедший в 2020...
Ну то есть то, что спецификация по функциональность может быть зафиксирована до окончательного издания стандарта три года назад, быть реализована ещё в 2018-м и, и, благодаря стандарту, работать одинаково и в GCC и в clang и в MSVS для программиста на Rust непостижимое явление, или только ви такой особенный?
Какой отношение история документа имеет к бинарному признаку поддерживаем/не поддерживаем стандарт?
Родителями становятся за 9 месяцев до непосредственно рождения ребёнка, вас же не удивляет, что одежду, пелёнки и прочее покупают заранее, а не после родов… или удивляет?
Зафиксированная и неизменная функциональность реализуется сразу после публикации, раньше чем выходит финальное издание стандарта, таким образом лаг между публикацией и поддержкой составляет несколько месяцев, а не три года
ну, поддержка стандарта 2020 года в компиляторах середины 2021-ого года оставляет желать лучшего. Например, модули реализованы только в gcc, и то частично, а изменения стдлибы реализованы только в msvc.
ну, поддержка стандарта 2020 года в компиляторах середины 2021-ого года

Например, модули реализованы только в gcc
Дата публикации стандарта и, в частности, финальной версии модулей — декабрь 2020 не смущает? :)
4 месяца на реализацию принципиально другого подхода к сборке, вы серьёзно? :)
Дата публикации стандарта и, в частности, финальной версии модулей — декабрь 2020 не смущает? :)
вы забываете что стандарт публикуется с весьма существенным лагом после финализации. Которая, емнип, была еще весной 2020.
вы забываете что стандарт публикуется с весьма существенным лагом после финализации. Которая, емнип, была еще весной 2020.

ну как бы, финальная версия предложения по модулям
Document number
ISO/IEC/JTC1/SC22/WG21/P1689R3

Date
2020-12-09

предыдущая редакция — pre-Prague была от января 2020

Ну так и называйте "поддержка стандарта pre-с++XX-1", "поддержка стандарта pre-с++XX-2", ..., "поддержка стандарта pre-с++XX-Y" почему она вдруг стала "поддержка стандарта с++XX"?


может быть… благодаря стандарту, работать одинаково и в GCC и в clang и в MSVS

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


Родителями становятся за 9 месяцев до непосредственно рождения ребёнка

Да ладно? А если роды преждевременные, то родители уже до зачатия? А материнский капитал они, как родители, потребовать могут? А если аборт? А то я всегда считал, что это "будущие родители". Само слово "родители", кстати, имеет весьма говорящий корень.


вас же не удивляет, что одежду, пелёнки и прочее покупают заранее, а не после родов

Ну вот и стандарт пишут до реализации в компиляторе, а не после… или нет?

почему она вдруг стала «поддержка стандарта с++XX»?
Наверное потому, что на тот момент стандарт включал только ту функциональность, нет? Может хватит позориться? :)

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


Если есть некий "тот момент", то и называется он "стандарт C++XX-тот-момент", а не "стандарт C++XX"

Так у нас стандарт имеет конкретное имя с точным набором функций или в него что левая пятка комитета захочет суют каждый день?
Я уже устал просить почитать хоть что-нибудь, а вы продолжаете стучаться в открытую дверь.
Само слово «родители», кстати, имеет весьма говорящий корень.
оффтопик, но корень — род
Ряд поколений, происходящих от одного предка, а также вообще поколение.

вы перепутали причину со следствием, что не удивляет.

Интересно, где? Учитывая, что я ничего не говорил, что из чего следует (собственные галлюцинации на этот счет не аргумент).


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

Ну то есть начали с того, «для шаблонов есть стандарт, см. туда», смотрим «туда», а там запросто может быть «ой, вы знаете, это еще не поддерживается и когда будет, не знаем. Нет, что вы, стандарт-то мы поддерживаем».
есть такая штука как feature test macros — как раз для разруливания частичной поддержки стандарта.
Сколько тут людей плакалось, что на проекте древний как гавно мамонта GCC и обновить его нельзя?
обычно когда люди плачут о древнем как говно мамонта GCC, он старше, чем первый стабильный релиз rust, но кажется вас это не сильно волнует…

А что должно волновать? Старше — значит вышел раньше, чем даже первый стабильный релиз rust, почему бы и не плакать?

А что должно волновать? Старше — значит вышел раньше, чем даже первый стабильный релиз rust, почему бы и не плакать?
в момент, когда кто-то выбирал условный gcc 4.8 в качестве компилятора, он при всем желании не смог бы использовать стабильный rust. Я даже удивлен, что это может требовать пояснений

Конечно не смог бы. И если он теперь не может обновится с этой версии компилятора на более новую, ему разве не нужно посочувствовать? Ведь его компилятор древнее даже первой версии rust-овского, это ж какая древность. Непонятно, какие именно еще пояснения к этому вы намерены давать.

И если он теперь не может обновится с этой версии компилятора на более новую, ему разве не нужно посочувствовать?
ведь наверняка проще переписать проект на раст, чем обновить компилятор с++
UFO just landed and posted this here
UFO just landed and posted this here
Рядом с основным проектом написать плагин, выдающий код, который может быть скомпилирован древней версией компилятора.

Это какие-то невероятные фантазии. Удачи написать "плагин" который код с async (одно из свежих значительных изменений) переделывает в код без async, и при этом ещё каким то чудесным образом можно использовать свежий рантайм tokio.

Нет, ну если задача так стоит — переделать новый код на Rust-е в старый код на Rust-е, то задача может быть велика, хотя и решаема. А вот на плюсах сделать шаблон, который Variadic шаблон (одно из значительных изменений в плюсах) сделает не Variadic, и чтобы он работал там, где поддержки Variadic-ов нет, принципиально невозможно. Все упадет еще на стадии лексического анализа.


Если задача скромнее — имея свой DSL генерировать по нему код, то вместо генерации кода с теми же asyncами генерировать код без них — очень даже подъемная.

А вот на плюсах сделать шаблон, который Variadic шаблон (одно из значительных изменений в плюсах) сделает не Variadic, и чтобы он работал там, где поддержки Variadic-ов нет, принципиально невозможно
Удивительно, и как же оно работало до появления С++11… да, было очень некрасиво с перегрузкой (или кучей макросов) на N+1 вариантов, но работало, значит не невозможно :)
Принципиально не реализуемая на С++03 фишка — move-семантика, остальное решается так или иначе.

Ну да, работало. Только свой код вам переписать придется, так как всякие Args... старый компилятор, увы, не понимает. И только средствами языка это не решить. У Rust-а компилятор не интересуется, что там передается на вход процедурному макросу (и если специально выбрать тип — что на вход декларативному макросу), можно и свой синтаксис изобрести, главное, чтобы скобки были сбалансированы. Т.е., используя только сам язык, подобная проблема решаема.

Ну это же просто бестолковое буквоедство. То что там формально макрос это часть спецификации с задачей такого уровня ну не помогает никак. Если надо компилировать новый С++ код старым компилятором то просто делаете так:


./trollface_cxx17_to_cxx03_сonverter code.cpp
g++ code.cpp

К тому же даже формально как вы сконвертируете код из подключаемого крейта через макрос в вашем коде? Такого в спеке раста нету — так что облом.


Но повторюсь, ваш trollface_macro_converter! это именно что троллинг.


upd: trollface_cxx17_to_cxx03_сonverter пишете на С++ так что используете только язык чтобы решить задачу.

Если надо компилировать новый С++ код старым компилятором то просто делаете так:

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


К тому же даже формально как вы сконвертируете код из подключаемого крейта

А зачем нам конвертировать чужой крейт? Это его автор должен делать. А мы пользуемся той версией крейта, которая поддерживается компилятором.


Это все равно, что попросить вас на C++ трансформировать шаблоны чужой библиотеки с помощью шаблонов вашего проекта.

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

принципиально невозможно
Ну да, работало.
Хорошо, пациент на пути к выздоровлению.
У Rust-а компилятор не интересуется, что там передается на вход процедурному макросу
В С++ тоже, это же макрос.
Тут уже приводили пример подобного плагина — Qt moc. Его даже умудрились переписать на С++14 если правильно помню, то есть решить все его расширения синтаксиса через шаблоны и макросы.
То, что Rust позволяет это сделать проще — круто, всё же сколько лет между созданием языков прошло? Но а дальше то что?
UFO just landed and posted this here
Может у них там встраиваемая система какая-то, компилятор для которой какой дали, тем и пользуйся, а другие просто не умеют под нее компилировать.

Если ты можешь скомпилировать под систему с помощью rustc то ты можешь скомпилировать с помощью последних clang++ и gcc с поддержкой новых фич. Если ты не можешь скомпилировать с помощью rustc то есть шанс что clang++ все еще работает, и есть больший шанс что и gcc работает. Если есть только какой-то вендорный компилятор то раста там нету и в помине, так что и вспоминать не приходится.


Иногда компиляторы при обновлении что то ломают, типа код работал быстро а стал медленно. Такое встречается везде и раст не исключение потому что для оптимизаций машинного кода там используется LLVM (кстати не давно был в расте баг что -march=native сюрприз-сюрприз замедлял код, и это была именно проблема растового фронтенда).


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

UFO just landed and posted this here
Портирование кода с gcc 6 на стандартный C++17 было на моём последнем месте работы отдельным проектом примерно на месяц, хотя там всего-то была этак сотня kloc кода, плюс одни из топовых спецов по плюсам, которых я встречал.
а что конкретно было причиной? Обычно в сотне kloc не так много мест, чтобы перевод на новый стандарт был проблемой. Если у вас только не использовался _GLIBCXX_USE_CXX11_ABI, но этот перевод должен был происходить лет 6 назад?
UFO just landed and posted this here
то есть этот процесс надо было назвать не «портированием кода с gcc 6 на стандартный c++17», а «портирование кода, обильно утилизирующего нестандартные расширения gcc, на clang (с параллельным включением с++17)»
UFO just landed and posted this here
Нет: там не использовались документированные расширения gcc
что вы такое пишете, что у вас на дистанции в 100kloc более пары утилизаций багов компилятора? Тем более таких, чтобы потом месяц переделывать
UFO just landed and posted this here
UFO just landed and posted this here

Плагин генерирует код в одном конкретно месте, на основе AST того кода который вы обернули в макрос. Чтобы плагин умел что-то делать подобное на С++ шаблоны (заметьте нужно это или нет я вообще не обсуждаю) он должен знать все обо всем коде. Поскольку он ничего не знает о коде за своими пределами он не может интегрироваться в язык, — в том смысле в котором это делают темплейты. Обычно связь макросов с другим кодом происходит посредством трейтов/дженериков. А дженерики это далеко не шаблоны.

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


Конечно ситуация улучшается, но со стороны это выглядит, как филигранное вытачивание костылей, чтобы не дай бог не задеть другие ажурные конструкции из костылей, на которых все и держится. И улучшение естественно возможно только в одном направлении — уменьшить всезнаемость этих шаблонов (которой на самом деле нет) и явно обозначить компилятору, где важные части, которые должны быть в сообщении об ошибке, а где нет.


Чтобы плагин умел что-то делать подобное на С++ шаблоны

Вот все говорят подобные фразы, а где примеры этого "чего-то подобного"? Пока я вижу как раз наоборот — в Rust-е есть, например, serde, реализующий фактически compile-time рефлексию, а вот плюсовые библиотеки для того же полагаются на особенности компиляторов. До поищите хотя бы статьи здесь, сколько было предложено велосипедов для сериализации, в везде нужно что-то делать руками и не в одну строчку.

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


Например если я вызываю my_template<X> то внутри темплейта может быть различная логика в зависимости от того X это int или std::string, или еще какие то более сложные свойства а ля есть ли конструктор копирования. Если я вызываю my_macro!(X) то макросу приходит только информация что X это какой-то индентификатор. Но на уровне макросов про него ничего не известно. Поэтому шаблон знает про икс (который объявлен в любом другом месте кода) много чего, а макрос ничего не знает.


а где примеры этого "чего-то подобного"?

Например та же специализация в расте используется в std либе для итераторов (чтобы оптимизировать Random-Access итераторы) не смотря на то что это найтли фича и в стейбл расте ее нету. В С++ это уже давно было и есть, начиная с С++11 наверняка. Только заметьте это реализуется не макросами а именно расширением возможностей дженериков в расте. Потому что на уровне макросов узнать есть Random-Access или нету невозможно.


serde это хороший пример удобной интеграции макросов в эко-систему (а не в язык, т.е. конечно можно сказать что интеграция в язык но в более общем смысле). По своей сути он ничем не отличается от Qt-moc или какой то генерации сериализаторов на основе protobuf спеки. То что для макросов в расте есть вполне конкретный формат аттрибутов, это тоже безусловно плюс, это лучше чем когда каждая либа лепит свой формат комментариев для этих целей.

По своей сути он ничем не отличается от Qt-moc
Вот только что бы Qt-moc работал с тем же MinGW, пропатченный компилятор входит в комплект поставки Qt, иначе «не заведётся», что тоже говорит о «стандартности» moc-а :)
Например если я вызываю my_template<X> то внутри темплейта может быть различная логика в зависимости от того X это int или std::string, или еще какие то более сложные свойства а ля есть ли конструктор копирования. Если я вызываю my_macro!(X) то макросу приходит только информация что X это какой-то индентификатор.

Не вижу в вашем примере, где в my_template передаются все эти "какие то более сложные свойства а ля есть ли конструктор копирования". Точно также как в в Rust-е всего лишь идентификатор.


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


В Rust-е же типаж — конструкция языка. В макросе ставите требование:


trait Clone : Sized {// это стандартная библиотека Rust
  fn clone(self) -> Self;
}
// потроха my_macro!(X)
fn do_generation() -> TokenStream {
  // Тут получаем токен, представляющий X, переданный в макрос
  let X = ...;
  // Макрос quote! генерирует код на Rust в нужном формате
  // из уже написанного Rust кода, плюс поддерживает интерполяцию
  // переменных, записанных через #variable
  quote! {
    // Объявляем функцию/тип которая требует определенных свойств
    // от типа, с которым работает... а ля есть ли конструктор копирования
    fn generated_code<T: Clone>(obj: T) {
      // Делаем что нам надо...
      obj.clone();
    }
    // Вызываем функцию с типом на входе макроса.
    // Заметьте, что проверять, можно ли это сделать, не надо,
    // это сделает компилятор в своем обычном цикле компиляции
    generated_code<#X>();
  }
}

Все. При компиляции получаем generic-функцию generated_code, которая работает только с типами, реализующими типаж Clone. Если передадите в макрос что-то не-клонируемое, то получите ошибку компиляции, вполне внятную.


Не обязательно эту generic-функцию генерировать — можно использовать уже существующую, написанную вне макроса.


Поэтому шаблон знает про икс (который объявлен в любом другом месте кода) много чего, а макрос ничего не знает.

Макрос знает ровно столько же, сколько шаблон, а иногда даже больше, если вы передаете в макрос не только идентификаторы, а часть синтаксического дерева Rust. Да даже просто уже можно знать, что у вас на входе Rust-строка, если вы вызываете макрос как


my_macro!("строка");

Только заметьте это реализуется не макросами а именно расширением возможностей дженериков в расте. Потому что на уровне макросов узнать есть Random-Access или нету невозможно.

Ну в принципе, невозможным не выглядит. Как-то так можно:


trait OptimizedAccess {
  fn skip(&mut self, count: usize);
}
impl OptimizedAccess for RandomAccessIter {
  fn skip(&mut self, count: usize) {
    // просто прыгаем на count элементов вперед
  }
}

impl OptimizedAccess for NonRandomAccessIter {
  fn skip(&mut self, count: usize) {
    // крутимся в цикле, пока не вычитаем count элементов
  }
}

trait Iterator {
  pub fn skip(self, n: usize) -> Skip<Self>
  // Полагаю, проблема в библитечном коде из-за этой строчки
  // Ее сложно добавить без слома обратной совместимости
  // В оригинальном типаже ее нет
  where Self: OptimizedAccess
  {
    OptimizedAccess::skip(&mut self, n);
    ...
  }
}

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

Я в курсе как работают макросы. И вы только что подтвердили все что я написал, что все решения делаются на дженериках+трейтах или шаблонах но никак не макросами.


По пунктам:


внутри макроса нельзя написать


if X.is_сlonable() {
quote! { ... }
} else {
quote! { ... }
}

внутри темплейта можно:


void f(auto x) {
if constexpr (is_copy_constructible_v<decltype(x)>) {
 ...
} else {
 ...
}
}

или же так


const s: &'static str = "cтрока";
my_macro!(s); // облом. уже не стринг литерал на входе

Т.е. по факту макрос не знает что вы передали строку.
Темплейт будет работать одинаково с обеими вариантами.


Ну в принципе, невозможным не выглядит. Как-то так можно:

В каком месте тут макросы? Опять трейты дженерики.


Если мы хотим обсуждать дженерики версуз шаблоны то это уже другой разговор.


По поводу RandomAccessIter/NonRandomAccessIter


Во первых сразу минус такого решения. Сейчас мне нужно чтобы имплементировать свой итератор просто сделать метод next() и уже готово. В вашем решении мне ещё надо будет делать impl OptimizedAccess и импелементировать метод skip(). При чём для итераторов у которых нету RandomAccessIter это будет каждый раз один и тот же код. А это не только метод skip() а ещё наверное штук пять. Может быть в таком простом примере вам удастся выкрутится с помощью того что в трейтах есть имплементация по умолчанию (такой частный случай специализации).


Во-вторых представим я делаю zip двух итераторов, если оба RA то результат должен быть тоже RA. При чём без специализации это будет вместо двух кейсов (if (оба ра) {} else {}) — четыре метода в расте. Если представить что кроме RA у итераторов есть еще парочку свойств на которые надо реагировать, то можно быстро получить комбинаторный взрыв.


Добавлено:


Вот вам пример по-проще на специализацию.

внутри темплейта можно:

Да нет, там все делает is_copy_constructible_v<decltype(x)>, что по сути вызов метода какого-то метода у типажа IsCopyConstructable, который должен реализовывать тип. Конкретно этот реализуется компилятором на основе формы типа для всех типов, в Rust-е аналогичным образом реализуются Send, Sync, Sized и некоторые другие. Так как парадигма Rust-а отличается от парадигмы C++, то без знания того, что там в ветках if аналог привести сложно. Если просто выбирается, как копировать тип (для чего еще проверять способ конструирования?), он может выглядеть просто как


fn f<T: Clone>(x: T) {
  x.clone();
}

В общем то да, никаких макросов в таком простом примере не требуется.


Темплейт будет работать одинаково с обеими вариантами.

В C++ шаблоны строки тоже не передаются как шаблонные параметры. Насколько я помню, только интегральные типы допустимы, а строка таковым не является.


Сейчас мне нужно чтобы имплементировать свой итератор просто сделать метод next() и уже готово.

Если захотите поддержки для RandomAccess со стороны стандартной библиотеки, каким-то образом вам все равно придется сказать, что ваш возвращаемый итератор — RA. И я уверен, это будет делаться через реализацию типажа, т.к. в этом их назначение — описывать свойства типов.


вашем решении мне ещё надо будет делать impl OptimizedAccess и импелементировать метод skip().

Покажите, как вы это сделаете принципиально иным образом.


При чём для итераторов у которых нету RandomAccessIter это будет каждый раз один и тот же код.

Как раз для решения этой проблемы и пилится специализация. Чтобы предоставить реализацию по умолчанию и возможность перекрывать эту реализацию для нужных типов. Сейчас это невозможно, т.к. реализации будут конфликтовать.


Во-вторых представим я делаю zip двух итераторов, если оба RA то результат должен быть тоже RA.

Да, думаю, что сейчас сложно сделать функцию, которая будет возвращать типы с разными свойствами на основе наличия свойств в других типах. С другой стороны, это не требуется, т.к. ZipIter все равно не должен сам как-то продвигать итераторы своих компонентов, а должен делегировать им эту работу. И там уж не важно, как эти компоненты работают. Изучать внешним кодом признак RA на ZipIter тоже не нужно — мы уже выяснили, что для этого нужно спрашивать реализацию OptimizedAccess, а ее реализовать тривиально:


struct ZipIter<I1, I2> {
  it1: I1,
  it2: I2,
}
impl<I1, I2> OptimizedAccess for ZipIter
  where I1: OptimizedAccess,
        I2: OptimizedAccess,
{
  fn skip(&mut self, count: usize) {
    self.it1.skip(count);
    self.it2.skip(count);
  }
}
Да нет, там все делает is_copy_constructible_v<decltype(x)>

Так это и есть темплейт внутри темплейта, здрасте. Была тема про темплеты vs макросы, а съехали на трейты. А я в самом первом сообщении написал что это три разных вещи.


без знания того, что там в ветках if аналог привести сложно.

Я не обсуждаю нужно это или нет (уже выше так и написал один раз). Я обсуждаю что можно сделать а что нет. Пример просто взял из головы, — первый type_traits что пришел на ум. Кстати в С++ оно через концепты или sfinae делается. Компилятор лишь предоставляет наличие метода с определённой сигнатурой. Но это не важно, в макросе у вас нету информации никакой всё равно. Она есть у компилятора уже потом когда макрос раскрылся.


Покажите, как вы это сделаете принципиально иным образом.

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


Сейчас это невозможно, т.к. реализации будут конфликтовать.

А чо макросы не помогли да? Это же плагин компилятора, который может всё. Как же так случилось...


спрашивать реализацию OptimizedAccess

Это не совсем так. На основе того что мы знаем о I1 и I2 мы можем оптимизировать ZipIter::next(). Если мы дергаем i1.next() и i2.next() — имеем две проверки на доход итератора до конца. Если мы знаем длину заранее то можно сделать n = min(i1.len(), i2.len()) в конструкторе ZipIter. Тогда можно сэкономить одну проверку. Плюс еще сэкономить махинации с заворотом и разворотом Option<Item>. Что и делают в std либе раста.
Кстати я там еще пример добавил про сравнение слайсов в предыдущем сообщении.


Изучать внешним кодом признак RA на ZipIter тоже не нужно

Кода вы делаете impl RA for ZipIter вы как раз навешиваете признак RA на собственно ZipIter :) А потом когда вы используете ZipIter вы изучаете что там у него есть, и в зависимости от этого делаете одно или другое… Когда вы объявляете MyClass(const MyClass& other) то навешиваете признак, потом в шаблоне изучаете, как раз тут принцип похож.

Так это и есть темплейт внутри темплейта, здрасте. Была тема про темплеты vs макросы, а съехали на трейты. А я в самом первом сообщении написал что это три разных вещи.

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


Более близкими задачами будут:


  • строить парсер по грамматике на этапе компиляции (ладно, в бусте аж 3 штуки есть, но вопросы времени компиляции и требуемую для этого память они как-то обходят стороной. Да и как выглядят ошибки я после быстрого просмотра документации не обнаружил).
  • декларативно навешивать атрибуты на код, типа такого.
  • да даже просто имена enum-ов получить.
  • или ту же сериализацию многострадальную.
  • логгинг — логгировать вход и выход из функции, с именем функции и параметров.

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

Ну со специализацией и не придется реализовывать вручную там, где не надо. В приводимых вами ссылках на std Rust-а она как раз и срабатывает на наличие типажа TrustedRandomAccess, как я и говорил. И даже сам этот типаж на типе Zip<I1, I2> реализуется, если оба итератора его реализуют, как вы и хотели.


А чо макросы не помогли да? Это же плагин компилятора, который может всё.

Он может все на синтаксическом уровне, т.е. писать код за вас. Но нарушить правила языка вам не поможет, скажем, побороть ненавидимый многими borrow checker не получится :).

Я предлагаю примеры на тему:
"что нельзя сделать с помощью плагина к компилятору, но можно сделать на шаблонах"


Вы предлагаете наоборот:
"что нельзя сделать с помощью шаблонов, но можно сделать на процедурных макросах"


Такой вот разговор получается)))
Я согласен, это должно друг друга дополнять в идеале.
Шаблоны можете заменить на более мощные дженерики если они появятся в расте.

UFO just landed and posted this here

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

Мой профессиональный опыт написания относительно современного С++ кода и аудита Rust-кода (включая Rust-код, который существенно использует unsafe) заключается в том, что безопасность современного С++ просто не сравнится с языками, в который безопасность памяти включена по умолчанию

Доработайте, пожалуйста, мысль. Безопасность в C++ всё же хорошая или плохая?
А почему ви спрашиваете, играясь с кубиками в руби? =) (с)

Articles