All streams
Search
Write a publication
Pull to refresh
-6
0
Иван Левашев @OCTAGRAM

Удалённый программист Delphi+Ada

Send message
И какое такое нужно управление ресурсами?

В том, что я видел с упором на безопасность и корректность, нет указателей. Либо глобально размещено, либо на стеке какой-то задачи. Вот в Ironsides, SPARK-верифицированном DNS-сервере нет указателей, потому что та версия SPARK, под которую он писался, их не поддерживает. Это не мешает ему использовать контейнеры. Просто это контейнеры такие специальные, которые параметризуются максимальной вместимостью и занимают сразу максимум места, а вместо указателей там индексы.

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

А если на один участок памяти может быть более одного указателя, статически не отождествимых с одним, то всё, тут уже понятно, что это какая-то другая область с расслабленными требованиями начинается.
Список runtime проверок, определённых стандартом языка Ада
Список дополнительных runtime проверок, определённых GNAT

Из не совсем runtime: protected может работать как рвлок. При этом на чтение со множественным доступом содержимое-таки действительно видно в режиме только чтение. Также может работать как монитор Хоара. При этом условные переменные сигналятся автоматом. Куча таких мелочей делают жизнь проще.
Из ParaSail собираются заимствовать уникальные указатели. Они семантически похожи на записи с дискриминантом, на них наконец-то удалось натравить хоть какую-то верификацию, а если что-то новое можно верифицировать, оно идёт в язык Ада и SPARK. Будучи экономическим фундаментом, SPARK — это обстоятельство непреодолимой силы. Разделяемые ссылки в этом смысле плохие, и в ISO стандарт не идут, а так в библиотеках пруд пруди.

www.dmitry-kazakov.de/ada/components.htm#Objects_etc
www.adacore.com/gems/gem-100-reference-counting-in-ada-part-3-weak-references

Ещё реализацию видел у Вадима Годунко в Матрёшке, а сам я пользуюсь собственной, которая работает как мне надо.
wrapping в релизе был чем-то настолько немыслимым, настолько редким специальным случаем, что я этот вариант даже не принял к рассмотрению. Нет проверок на числах — нет чисел. Нет чисел — нет программы.
В поисках лучших практик чтиво по C++ будет болтаться где-то в конце списка.
Те, кто приходят с других языков и по привычке смотрят, что там с указателями, смотрят не туда. Предполагается, что в ультраидиоматичных адских программах голые указатели вообще не проскакивают. А ресурсами управляют, например, контейнеры. Лежит в карте где-то запись, навёл на неё Cursor, написал declare-begin-end, между declare и begin прорубил через renames окно (Ada 2012 reference) в эту запись, и работаешь с ней. При включенных защитах от tampering это окно ещё по принципу RAII удерживает блокировку на контейнере. Как из end вышел, всё, нет окна, нет блокировки, кроме, разве что, от Cursor.
Глобальные обработчики прерываний, цепочки обработчиков прерываний, DOS, 15дюймовый электроннолучевой дисплей формата 4:3, Turbo Pascal, EGA. Сколько воспоминаний.

Это, я так понимаю, тот самый глобальный обработчик, которым не комильфо рулить из библиотек, но и оставлять как есть не хочется.
Из тех альтернатив, которые я видел, кажется, что именно в языке Ада сделан удобный выбор. Либо anything can throw. Либо SPARK верифицировал критичный к производительности код, и это даёт мандат на отключение runtime проверок в этой части кода. Как в libsparkcrypto. Или вот ещё, 13й слайд.

Какого-то компромисса здесь не просматривается. Архитекторы Rust, мне кажется, промахнулись, пытаясь нащупать таковой. Чтоб самый обычный плюс и минус, которые везде, были спусковыми крючками апокалипсиса, ну такое. Мне кажется, потенциальным программистам на Rust надо лучше объяснять, в какую петлю они суются.
А, понятно. Исписываем всё в checked_add, checked_sub и checked_mul и думаем, чёрт возьми, вот она, эргономика. Правда, при этом чужие, не столь эргономично написанные библиотеки предательски норовят выдать панику.
catch_unwind — это тот самый, который срабатывает уже после того, как консоль загажена
Где взять плюс, минус, умножение для чисел, чтоб возвращали Option или Result? Я что-то не нашёл.
В связи с Better-C можно ещё упомянуть CCured и Deputy. CCured — это такой транслятор Си->Си, который делает указатели жирными, но по возможности как можно менее жирными. Специальный анализатор отслеживает все потоки данных и выбирает степень жирности каждого указателя, которой было бы достаточно для того, чтобы все требуемые операции над ним были безопасно реализуемы. Самый плохой указатель WILD, и он как чума всё заражает. Внутри структур, на которые ссылается WILD, могут быть только WILD указатели.

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

Из всех проектов по отмыванию унаследованного кода от грязи это самый продвинутый. На пути отмывания от грязи следующим этапом после принуждения к инкапсуляции памяти, казалось бы, должен быть порт на Аду, но нет, Ada is not invented here. В качестве самодостаточного языка программирования, не для старого кода, а для нового, у них самоделка Deputy по тем же лекалам, что и CCured. Для полноты можно упомянуть.

С C++ подобную штуку, похоже, невозможно нормально скрестить, так как и шаблоны, и анализатор минимальной жирности входят в конфликт. Единственно работающие способы отмыть C++ от грязи я вижу в CHERI и Эльбрус2000, то есть, максимально жирные указатели для всего. В то время, как в CCured большую часть указателей автоматом удаётся облегчить, а дальше разработчик устраняет оставшиеся WILD, в C++ эта работа даже не может начаться. Писать на C++ это такое попадалово, что потом вовек не отмыться. К сожалению, никто (Cyclone и др. отмывальщики C) не пошёл другим путём, в сторону Objective-C. Там, похоже, таких проблем, как с C++, нет, и можно было бы наполнить экосистему безопасным кроссплатфоренным GUI и программами на нём.

Для надёжных языков программирования поднятый вопрос актуален, потому что каждый bzip2 с нуля быстро не перепишешь, а если подключишь неотмытый libbzip2, то через него-то тебя и ломанут. Надо исследовать возможности, как отмыть унаследованные библиотеки на Си перед тем, как подключать их в свой код на надёжном языке.
Да и в игре пусть лучше поведение NPC попадёт в сюжет Мармока, чем аварийно завершит всю игру.
SPARK был упомянут, но можно что-то ещё, да
Тут ещё надо уточнить, что такое паники. Вот взять Delphi, Ada, Rust. Казалось бы, везде проверка целочисленных переполнений и диапазонов есть.

Но если вдаться в детали, в Delphi и Ada оно приводит к легко уловимому exception, а в Rust случается паника. А паника в Rust не то же что исключение. Для отлова паники можно использовать функцию, принимающую два замыкания, первое — что нужно сделать, второе — что сделать, если поймали панику. Кажется, это похоже на обработчик исключений, но нет, опять не угадали. Перед тем, как упасть вниз по стеку, до точки, где установлен перехват паник, ещё успевает нагадить в консоль обработчик паник. Насколько я понимаю, этот обработчик тоже можно отключить или заменить более молчаливой версией. В программе такое можно сделать, а в библиотеке? Нормально ли будет, если какая-то библиотека заменит глобальный обработчик паник? Или если будет полагаться на то, что он отключён, и преспокойно себе бросать и ловить паники.

Всё это похоже на Turbo Pascal образца конца 80х. Там тоже целочисленные переполнения и выход за границы диапазона были. НО ВАЖНО КАК ОНИ БЫЛИ. Там же не было структурной обработки исключений. Там было прерывание, прерывание можно было гипотетически перехватить. Структурной обработки исключений нет, поэтому что делать обработчику прерываний, интересный вопрос. Лучшее, что можно было придумать, это на SetJmp/LongJmp сделать что-то вроде SEH с RAII. Как и в случае Rust, не приходится ожидать, что это сделано в библиотеках.

Так что по факту этот механизм не работает, или работает отнюдь не в полную силу, не так, как в других языках программирования.
Языки есть с одним владельцем. Он там поливает территорию вокруг себя, делает благообразно. Но чужие там не ходят. А есть демократичные. Там больше одного производителя, но и анархия. Везде свои сильные и слабые стороны.
На моей памяти это первый и последний попробованный мною компилятор, который крашился.
Какая там может быть надёжность, если они даже опечатку поймать не могут. Серёзно?
Вообще говоря, у Delphi был неплохой задел. Но Delphi — это два языка в одном. Есть один язык, в котором и есть те самые неплохие заделы, и есть язык, который поддерживается.

В первом языке проверка целочисленных переполнений и диапазонов. В первом языке открытые массивы (в терминах ISO Pascal согласованные массивы, conformant arrays). В первом языке отличные от чисел перечисления. В первом языке ARC существует пусть хотя бы на COM интерфейсах.

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

Второй язык развивается и продвигается, а первый язык прозябает в стагнации и безвестности. Вот, например, как были со времён Турбо Паскаля проверки целочисленного переполнения и диапазонов, так и остались почти без изменений. Ну разве что Assert добавились. А что, больше проверять нечего стало? nil можно было бы не ломиться разыменовывать, чтоб упереться в EAccessViolation, а проверить. А ещё, как в Ada 2005, можно дать синтаксис, чтоб запретить nil. В Ada 2005 not null может стоять везде, хоть в record. Это возможно сделать, потому что в языке Ada есть три фичи, которые делают возможным и удобным обращаться с такими структурами данных.

  1. Во-первых, при объявлении record можно сразу же и задать значение по умолчанию. Скорее всего, так не будет сделано, но возможность есть.
  2. Во-вторых, есть агрегаты, которые можно использовать везде, не только при инициализации глобальных переменных. И если значение по умолчанию не задано, а null невозможно принять по умолчанию, потому что он запрещён not null, разработчик может использовать агрегат.
  3. В-третьих, можно инициализировать любую переменную, не только лишь глобальную. В языке Ада это с первых версий, с 1983го года, в Delphi это появилось только в этом 2019м году, в версии 10.3 (Rio).


За счёт этого компилятору Ады есть, к чему принудить разработчика. Если же в Delphi всего этого не было, то пропустив азы, сразу в мир надёжности не вскочишь, но хотя бы в аргументах функций можно было бы nil запрещать. Могли. Но не сделали.

Идём далее. В ISO Pascal есть такая возможность, как conformant array parameters. В языке Ada она специального имени не имеет, но unconstrained arrays, если их принимать формальным аргументом, так себя ведут. Существует изначально, с 1983го года. В языке Delphi это называется open arrays. Существует с 90х, версия Delphi 4 или около того. В C++ что-то похожее появилось только в C++20, std::span. Можно такие параметры из обычных массивов делать, можно из динамических. Можно передать по цепочке принятый формальный аргумент такого типа. А ещё можно сузить, можно передать поддиапазон значения. Дебилизм в Delphi состоит в том, что псевдофункция Slice, единственный способ скрафтить суженное значение, может обрезать справа, но не слева, в отличие от Copy, создающей новое значение. Какого-то обоснования такому положению вещей я не вижу. Манипулируя с указателями, я могу добиться того, чтобы в функцию-таки передать обрезанный с другой стороны подмассив, но тогда компилятор не вполне способен проверить, не было ли выхода за границы диапазона. Это выглядит, будто тёмный угол, в который давно никто не заглядывал.

Ещё по мелочи хотелось бы, чтоб Boolean(5) не работал, а бросал исключение.

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

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

А вот вы знаете, когда вашего Rust ещё даже в проекте не было, у тех, кто программировал на Delphi, уже были и проверки диапазонов, и отлов целочисленных переполнений, и даже с барского стола перепали open arrays. Мы, правда, будучи кусками дебилов, забыли включить их по умолчанию, и чтоб уж дебилизм был последовательным, прорекламировать включить тоже забыли, но теперь-то мы исправимся. И по умолчанию включим, и Slice починим, и всё это последовательно будет, а не спонтанными подачками.


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

Information

Rating
Does not participate
Location
Воркута, Коми, Россия
Date of birth
Registered
Activity