Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Переносимый Ассемблер (Си) _ Двоичные потоки

Автор: OCTAGRAM 11.09.2019 17:31

GNAT -gnat95

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



За основу берётся пакет https://www.adaic.org/resources/add_content/standards/95lrm/ARM_HTML/RM-13-13-1.html. Более конкретно, оттуда импортируются типы. И по аналогии с теми методами делаются методы здесь.

Аналогично Sample_Reference, делаются два типа. Один — для наследования, Root_Stream_Referenced. Другой — обёртка Root_Stream для применения в будущих пакетах.

Для интеграции с Адой в одну сторону делается Ada_Stream, обёртка-наследник стандартного Ada.Streams.Root_Stream_Type. Обёртка не реализует по второму разу Root_Stream, а содержит её в публично доступном поле и просто делегирует вызовы методов.

В обратную сторону в общем случае не получится, и делается только один частный случай, File_Stream_Referenced, содержащий https://www.adaic.org/resources/add_content/standards/95lrm/ARM_HTML/RM-A-12-1.html.

Как и всегда при использовании механизма сильных ссылок, делаются попарно два типа, для динамической памяти File_Stream_Referenced и обёртка File_Stream. API изменяется.

    procedure Create (File : in out File_Type;
Mode : in File_Mode := Out_File;
Name : in String := "";
Form : in String := "");


Create превращается в функцию, возвращающую сильную ссылку. Form убирается. Тип Name заменяется на String_32. Можно объявить subtype String_32 is Wide_String, чтоб собиралось. Wide_String приводится к String конструкциями вида Character'Val (Unsigned_8'Mod (Character_32'Pos (Element))). Работать, конечно, с Юникодом оно нормально так не будет, но API правильное.

Аналогично с Open.

Delete не нужен. Close в явном виде не нужен, а неявно он вызывается из Finalize после проверки Is_Open. В Finalize нужно также вызвать родительский Finalize.

Унаследованные Read и Write переопределяются.

Функция Stream выбрасывается. Read и Write реализуются без неё, а если нужен адский поток, то обёртка для этого уже сделана выше.

function Name приводится к String_32 дубовым способом, как и в Create.

function Form выбрасывается.



Задача мне: надо дать заглушки для поддержки Ada 2005+ фич в AdaMagic. Я писал такое, но не под рукой сейчас

Автор: Sergey Dukov 11.09.2019 17:53


Задачу в первом приближении понял. Займусь несколько позже.
Сейчас занимаюсь векторами. К стати, в АДА2012 вектора могут работать с потоками. Реализовать эту функциональность для наших векторов?

Автор: Sergey Dukov 21.10.2019 13:03

Раньше мне вроде бы было всё понятно. Сейчас у меня новый уровень понимания -- почти ничего не понятно. Зачем множить ссылки на файл? Лишь для того, чтобы деструктор в конце закрыл файл? Или каждый экземпляр ссылки должен иметь свои собственные атрибуты (типа позиции файла и моды файла)? Тогда нужно ли вводить спин-блокировку на операции, изменяющие эти атрибуты? Как нужно порождать новые экземпляры ссылок, специальной функцией типа "New_Instance" или простым копированием ссылки? Нужно ли вводить процедуру отсоединения ссылки от файла типа "Release_Instance" или "Detach_Instance"? Как реализовывать функциональность пакета, простым вызовом процедур из пакета "Ada.Streams.Stream_IO" или разработать независимую реализацию? И это, думаю, далеко не полный список вопросов.

Автор: OCTAGRAM 25.10.2019 0:12

Цитата(Sergey Dukov @ 21.10.2019 13:03) *
Раньше мне вроде бы было всё понятно. Сейчас у меня новый уровень понимания -- почти ничего не понятно. Зачем множить ссылки на файл?


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

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

Цитата(Sergey Dukov @ 21.10.2019 13:03) *
Лишь для того, чтобы деструктор в конце закрыл файл?


Да, это тоже важная задача.

Цитата(Sergey Dukov @ 21.10.2019 13:03) *
Или каждый экземпляр ссылки должен иметь свои собственные атрибуты (типа позиции файла и моды файла)?


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

Цитата(Sergey Dukov @ 21.10.2019 13:03) *
Нужно ли вводить процедуру отсоединения ссылки от файла типа "Release_Instance" или "Detach_Instance"?


На текущий момент нет.

Цитата(Sergey Dukov @ 21.10.2019 13:03) *
Как реализовывать функциональность пакета, простым вызовом процедур из пакета "Ada.Streams.Stream_IO" или разработать независимую реализацию? И это, думаю, далеко не полный список вопросов.


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

Автор: Sergey Dukov 25.10.2019 0:57


Насколько я понял, задача такова:
Упрятать значение типа "Ada.Streams.Stream_IO.File_Type" в объект некого другого типа, имеющий счётчик инстанций. Новые инстанции открытого объекта получать простой операцией присвоения.
А как назвать новый тип? Может быть "Referenced_File"?

Автор: OCTAGRAM 25.10.2019 4:03

Цитата(Sergey Dukov @ 25.10.2019 0:57) *

Насколько я понял, задача такова:
Упрятать значение типа "Ada.Streams.Stream_IO.File_Type" в объект некого другого типа, имеющий счётчик инстанций. Новые инстанции открытого объекта получать простой операцией присвоения.
А как назвать новый тип? Может быть "Referenced_File"?


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

Большой объект в памяти File_Stream_Referenced наследуется от абстрактного Root_Stream_Referenced, и автоматически получается limited.

Пользователям по возможности раздаются сильные ссылки File_Stream. У File_Stream_Referenced есть конструирующие методы Create и Open. Ведь функции не могут возвращать limited в Ada 95. Значит, методы. access File_Stream_Referenced у них первым аргументом, как и всегда.

Эти методы заворачиваются в функции, возвращающие сильные ссылки. То есть, на первом плане есть функции Create и Open, возвращающие сильные ссылки на File_Stream. Вот File_Stream-то копируемый.

Должно быть возможно написать

New_Stream : File_Stream := Open (выражение типа Wide_Wide_String, режим файла);


У сильной ссылки File_Stream должен быть метод Upcast, приводящий её к сильной ссылке Root_Stream на абстрактный поток.

Автор: Sergey Dukov 25.10.2019 5:48

Я тут подумал, что на будущее стоит ввести в пакет "Unbounded_Array" процедуры работы с UTF16, чтобы потом реализовать открытие и работу с файловыми потоками через стандартную СИ-шную библиотеку которая поддерживает UTF16.

Автор: Sergey Dukov 25.10.2019 10:42

Я проанализировал открытие файла потока в реализациях GNAT и в AdaMagic, и пришёл к выводу что заморачиваться с UTF16 не имеет смысла. Функция _wfopen имеется только в СИ-библиотеке MSVCRT. В GNAT дескриптор потока AFCB имеет поле Encoding, которое описывает кодировку поля имени. По умолчанию это -- UTF-8. Само открытие потока осуществляется функцией fopen (импортируемое имя "__gnat_fopen"), которая третьим параметром имеет значение Encoding. Эта функция анализирует соответствующие условия в целевой ОС и осуществляет перекодировку строки имени файла с помощью функций библиотеки libiconv. Так что в GNAT нет проблем с открытием потока с UNICODE-именем. В стандартной реализации исполняющей библиотеке AdaMagic просто вызывается функция fopen из <stdio.h>, которая обычно не воспринимает UNICODE-имени. Так что AdaMagic нужно переопределить реализацию открытия потока и эта реализация будет зависеть от целевой ОС.

Автор: Sergey Dukov 27.10.2019 1:30

Я реализовал задание в пакете "File_Stream_IO". О том, что явный вызов процедуры "Close" не нужен, я помнил, а о процедуре "Delete" забыл. Реализовав эту процедуру, я долго думал как устранить конфликты между инстанциями потока в результате вызова процедуры "Delete". Посмотрел первоначальное задание и просто удалил реализацию этой процедуры. Как это у вас получается -- предусматривать всё на свете?
При реализации процедур с UNICODE-именами я как бы предположил, что стандартный пакет "Ada.Streams.Stream_IO" воспринимает эти имена в кодировке UTF-8. Для GNAT -- это верно, а для других компиляторов может потребоваться отдельная реализация этих процедур.
Жду дальнейших заданий.

Автор: OCTAGRAM 31.10.2019 9:15

UTF-16 в рантаймах, сейчас мне кажется, не надо

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

Автор: Sergey Dukov 31.10.2019 9:34

Я уже говорил, что с UTF-16 не стоит заморачиваться. Лучше сделать как в GNAT. Там анализируется текущая локаль и делается перекодировка с помощью функций библиотеки LIBICOVN. Кстати, ASCII -- подмножество UTF-8.

Автор: OCTAGRAM 31.10.2019 9:44

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

Вот, например, gprbuild, помню, перестаёт работать, если где-то в пути есть кириллица. Где-то как UTF-8 всё прочиталось, а где-то забыли, и в другое API всё пошло, где как ANSI интерпретация. Если не размежеваться с однобайтовыми кодировками, этот раздрай так и будет. Уж либо не делать, либо делать как следует.

Сейчас как следует делается наше API, а реализация не делается никак. 32битные символы, которыми задаётся имя файла, нужно через mod 256 приводить к номеру символа Character. Это моё текущее понимание «никак»

Автор: Sergey Dukov 31.10.2019 9:53

Я подключил API из нашего пакета Unbounded_Array и для ASCII оно так и получится.