Скачал я себе ГНАТ (Gnat GPL(без исходников, поленился все ссылки тыкать) и Win32Ada.
Скопировал я для начала в редактор кода текст из Википедии:
with Ada.Text_IO;
procedure Hello is
use Ada.Text_IO;
begin
Put_Line("Hello, world!");
end Hello.
[2011-01-18 21:32:02] Could not determine the project for file: C:\ADA\projects\Test\test.gpr
[2011-01-18 21:32:02] Invalid context, cannot build
Аааа.... Вот это:
Мда, название, как оказалось, у языка очень неудобное - в гугле невозможно ничего найти, сплошная эзотерика да некромантия выдаётся.
Я ничего не нашёл, всё пока делаю наугад. Какой учебник посоветуете? Что-нибудь мощное и подробное, типа Фаронова?
Пока пытаюсь понять работу основных операторов.
with Ada.Text_IO;
use Ada.Text_IO;
procedure Test is
i: integer;
begin
i := -7;
Put_Line(integer'Image(i));
i := i / 2;
Put_Line(integer'Image(i));
end Test;
with Ada.Text_IO;
use Ada.Text_IO;
procedure Test is
i: integer;
j: float;
begin
i := -7;
Put_Line(integer'Image(i));
j := i / 2;
Put_Line(float'Image(j));
end Test;
function "/" (Left, Right : Integer'Base) return Integer'Base is
type Add_Type is array (Boolean'Range) of Integer;
Add_Value : constant Add_Type := (False => 0, True => 1);
begin
return Standard."/" (Left, Right) - Add_Value ((Left rem Right) < 0);
end "/";
pragma Inline("/");
> берешь и переопределяешь операцию деления:
Вот уж от кого, а от Ады не ожидал.
В Дельфе долго не хотели вводить, типа ради безопасности и надёжности, а тут-то...
Надежность ни разу не страдает. Скорее наоборот. Помнишь, я в начале той темы приводил пример с перепутанными временем и расстоянием? Как вариант решения проблемы приводилось решение:
procedure Check_Speed(T : Time; D : Distance) is
S : Speed := Speed(Integer(D) / Integer(T)); -- явное приведение типов !!!
begin
if S < SAFETY_SPEED then
Increase_Speed(S); -- Ok
end if
end Check_Speed;
function "/" (Left : Distance; Right : Time) return Speed is
begin
return Speed(Integer(Left) / Integer(Right));
end "/";
procedure Check_Speed(T : Time; D : Distance) is
S : Speed := D / T;
begin
if S < SAFETY_SPEED then
Increase_Speed(S); -- Ok
end if;
end Check_Speed;
Кстати, а как обстоит дело с размером получаемых файлов?
400 кб на консольную программку - это перебор, например.
Ну, почему 400? Если переключить режим с Debug на Optimize, а потом зайти в Project -> Edit project properties -> Switches -> Ada linker, и там включить Strip symbols - то будет в три раза меньше. Опять же, UPX никто не отменял, после его прогона остается 60Кб.
А уже если зайти в Switches -> Binder и слинковать программу с GNAT-овским рантаймом, то простенькое консольное приложение получится вообще порядка 15-17Кб, безо всяких UPX-ов.
Возникли вопросы по использованию отладчика.
Если нажать Ф5 или Ф6, то выдаётся ошибка, типа приложение не было запущено, хотя я нажимал "отладка-инициализация".
Начал читать Гавву (порадовало, что можно писать аналог const e=exp(1), в дельфи компилятор не умел вызывать функции для констант (приходилось экспоненту писать руками, типа 2.71828итд), ещё что фиксированная запятая включена в стандарт), но ещё по поводу языка возник вопрос, как пользоваться сущностями неизвестной заранее длины.
Пока единственный способ корректно их определить, который я нашёл - это заводить свой блок каждой такой переменной и инициализировать при объявлении:
declare S: string := My_Cool_Func_Which_Returns_String_With_Unknown_Length;
begin
Do_Something_Cool_With_S;
end;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
function F return Unbounded_String is
begin
return To_Unbounded_String("test");
end F;
ubs : Unbounded_String;
-- ...
ubs := F;
-- работай с ubs дальше...
> У Гаввы не описываются строки переменной длины, почему - не знаю, они уже были в Стандарте 95 года.
А, я уж испугался, что для цикла
for i := 0 to 255 do s := s + char(i);
придётся писать функцию с рекурсией, как во всяких прикольных языках типа Хаскела.
А если эта неограниченная строка (представляет собой, я так понял, указатель) сама делает очистку, то получается, что копирование её ни к хорошему не приведёт - копия после удаления оригинала будет указывать на порченные данные. То есть для них запрещёно присваивание (я дочитал до места про эту директиву в описании типа) и есть спец. функция копирования?
Ты вот это имеешь в виду, что-ли:
procedure Main is
ubs2 : Unbounded_String;
procedure P is
s : Unbounded_String;
begin
s := To_Unbounded_String("just a test");
ubs2 := s;
Put_Line ("Inside : " & To_String(ubs2));
end P;
begin
P;
Put_Line ("Outside : " & To_String(ubs2));
end Main;
Всё нормально отработало.
А в чём секрет фокуса?
Если бы я не прочитал, что переопределять := запрещено, то я бы всё понял - в присваивание зашили увеличение счётчика ссылок. Неужели разрешили?
Хранение данных произвольной длины без указателя?
Ничего не понимаю.
Для Controlled-типов есть процедура Adjust, с операцией присваивания ни разу не обязательно заморачиваться. Вот на нее и повешено увеличение счетчика ссылок.
> Для Controlled-типов есть процедура Adjust
И она неявно вызывается после присваивания (я ещё не дочитал до той главы)?
Кстати, а тут можно делать такой прикол, любимый сишниками, как рекурсивные шаблоны?
Да, она неявно вызывается, как только объект контролируемого типа появляется слева от знака присваивания:
Ух ты, тут можно хранить автоматические объекты в вариантных полях! С++ обломе!111
(ну да, с таким жёстким контролем обращения к вариантым полям вполне понятно)
Насчёт шаблонов я немного не понял. Чтобы определить, например, шаблонную функцию сложения двух чисел, мне какое ограничение надо поставить на тип? Я думал, допустимость операций над типом определится при инициализации шаблона.
generic
type Element is private;
with function "+" (Left, Right : Element) return Element is <>; -- Использовать стандартную для Element
function Sum(A, B : Element) return Element;
function Sum(A, B : Element) return Element is
begin
return A + B;
end Sum;
function MySum is new Sum(Integer); -- Здесь все нормально, у Integer-а есть "+"
function OtherSum is new Sum(Float); -- Здесь тоже нормально, "+" присутствует
function TryThisSum is new Sum(String); -- А вот тут - облом. Нет предопределенного "+"
> Хитрый, да?
Нет, просто со сложившимся мнением по другим (не будет показыать пальцем) языкам.
> Только обходится очень просто: не надо ограничивать тип, просто задай функцию, которая будет использоваться при вычислении суммы.
А, и эта функция будет находиться сама по имени, задавать её при инициализации шаблона не надо.
Тогда жить можно.
Я тебе больше скажу, можно даже сделать так:
function StrSum is new Sum(Unbounded_String, "&");
То есть при инициализации шаблона можно не только использовать операции по умолчанию, но и вставлять свои?
В С++? Ну класс с нужной функцией в шаблон же пихать можно, вот например, так.
Среда ругается, когда видит в коде русские символы (даже если в комментариях), что делать?
Поковырял исходники строк переменной длины.
Заметил, что для удлинения строки буфер растёт по экспоненте (с базой 33/32), что хорошо. Не понял константу Standard'Maximum_Alignment (не нашёл описание).
Не понравилось, что для оператора & нет проверки, что левый аргумент и результат указывают на один объект (ну чтобы с нуля строку не копировать), но тут, я так понял, в языке нет способа проверить это.
Я не смог определить оператор ":=>[]" (ну захотелось красивый оператор завести), но это ладно, баловство.
Ещё не получилось сделать так:
generic
type KeyBase is (<>); -- любой дискретный тип
...
-- где-то тут определяется aNode, не суть;
...
type KeyArr is array (KeyBase) of aNode;
Чтоб среда не ругалась на кириллицу, я себе переключил в настройках IDE Character Set в "Unicode UTF-8". Ну, или с кодировкой отдельно взятых файлов поиграйся (правой кнопкой мыши в редакторе -> Properties)
procedure Main is
type Int_Ptr is access Integer;
type Int_Ref is access all Integer;
AI : aliased Integer; -- косвенно доступная переменная. Статическая !!!
P_Ptr : Int_Ptr;
P_Ref : Int_Ref;
begin
-- попробуем получить ссылку на AI через Int_Ptr
P_Ptr := AI'Access;
-- Облом: access - только для дин. переменных
-- А теперь ссылку на AI через access all ?
P_Ref := AI'Access;
-- Все нормально, ошибки нет...
-- ...
end Main;
> Где именно ты пытаешься описывать KeyArr? Внутри пакета? Или снаружи? Или это у тебя вообще не пакет?
Да, это пакет.
Да, внутри пакета.
При загрузке я выбрал "открыть существующий проект" и убрал галочку "показывать при каждом запуске", думая, что сохранится последний выбранный вариант. Вместо этого среда (при запуске) создаёт новый файл, диалог открытия показывает по умолчанию "мои документы" (директорию не запоминает), после долгого блуждания по папкам и открытия проекта при попытке компиляции выдаёт gnatmake -d -P C:\Documents\ and\ Settings\ТаÑаÑÐ\default.gpr (и не находит его там, понятное дело), хотя файл был открыт в другой папке. Откуда привязка к "моим документам"?
Добавлено через 7 мин.
Так, про описание типа проехали, после подробного описания всего подряд компилятор таки съел массив с диапазоном неизвестного типа.
Осталась проблема с папками по умолчанию.
Открой GPR-файл своего проекта (можешь просто текстовым редактором, можешь - правой кнопкой мыши по корневому узлу дерева, там Project -> Edit sources file), и посмотри, что указано в use у Object_Dir. Если там указано "C:\Documents and Settings ..." - то просто зайди в свойства проекта и поменяй на вкладке Objects путь к папке, в которой проект будет собираться...
Покорение отладчика пока не удалось.
No symbol table is loaded. Use the "file" command.
Если при компиляции ошибка, то он не перекидывает сам на ошибку, приходится искать вкладку Locations, листать её вниз и среди всех сообщений искать те, что относятся к последней компиляции.
На мониторе 1024 на 600 меню занимает слишком много места (на код остаётся... треть высоты!). Окно с сообщениями тоже пестрит информацией, среди которой не найти нужную.
По поводу автотипов. Я так понял, слова Initialize, Adjust и Finalize зашиты в язык?
И почему-то ничего не выводит такой код:
-- auto.ads
package Auto is
type Auto is private;
private
with ADA.Finalization; use ADA.Finalization;
type Auto is new Controlled with record
null;
end record;
procedure Initialize(a: in out Auto);
procedure Finalize(a: in out Auto);
end package;
with ADA.Text_IO; use ADA.Text_IO;
with ADA.Finalization; use ADA.Finalization;
-- auto.adb
package body Auto is
procedure Initialize(a: in out Auto) is
begin
Put_Line("initialization");
end;
procedure Finalize(a: in out Auto) is
begin
Put_Line("finalization");
end;
end package;
-- test.adb
with Auto; use Auto;
procedure Test is
begin
declare
a: auto;
begin
null;
end;
end Test;
А, не, это опять что-то среда запускает только старый екзешник. То есть пытается.
[2011-01-26 11:44:22] Error while trying to execute C:\Program Files\ADA\projects\TEST\debug\test: not an executable
А сообщение не высветилось.
(для сравнения: так у меня настроена Д7, я и про размеры шрифта, полей, и про подсветку синтаксиса)
И кстати, почему C:\Program Files\ADA\projects\TEST\debug\test
а не C:\Program Files\ADA\projects\TEST\test.exe
?
А то я в этих вкладках совсем запутался, к тому же там многие галочки друг друга дублируют.
Правка: перезапустил среду. Оказалось, что тот код не компилировался, но сообщения не выводились. Перекомпилил, наконец пошло.
gnatmake -d -PF:\\Programs\\Ada\\forum\\forum.gpr main.adb --subdirs=debug -cargs -g -O0
gcc -c -g -gnatVn -gnat05 -gnata -g -g -O0 -I- -gnatA F:\Programs\Ada\forum\main.adb
main.adb:177:04: warning: variable "P_Ptr" is never read and never assigned
gcc -c -g -gnatVn -gnat05 -gnata -g -g -O0 -I- -gnatA F:\Programs\Ada\forum\myobj.adb
gcc -c -g -gnatVn -gnat05 -gnata -g -g -O0 -I- -gnatA F:\Programs\Ada\forum\mypck.ads
gnatbind -shared -I- -x F:\Programs\Ada\forum\debug\main.ali
gnatlink F:\Programs\Ada\forum\debug\main.ali -shared-libgcc -g -g -o F:\Programs\Ada\forum\debug\main.exe
[2011-01-26 12:04:32] process terminated successfully (elapsed time: 00.98s)
with Ada.Finalization; use Ada.Finalization;
package Auto is
type Auto is private;
private
type Auto is new Controlled with record
null;
end record;
procedure Initialize(a: in out Auto);
procedure Finalize(a: in out Auto);
end Auto;
with Ada.Text_IO; use Ada.Text_IO;
package body Auto is
procedure Initialize(a: in out Auto) is
begin
Put_Line("initialization");
end;
procedure Finalize(a: in out Auto) is
begin
Put_Line("finalization");
end;
end Auto;
with auto; use auto;
procedure Main is
begin
declare
A : Auto.Auto;
begin
null;
end;
end main;
> Gnatmake -> (галка установлена)"Debug Information"
Ada -> (галка установлена)"Debug Information"
Ada Linker -> (галка установлена)"Debug Information" и (галка снята)"Strip Symbols"
Что-то много дублирующихся галочек.
Мне бы вообще в идеале хотелось два положения - полная отладка и полная оптимизация.
(посмотрел, у меня третья галочка, оказывается, не стояла, а первые две стояли).
> Это еще более странно. Не перебрасывать на Location и на первую из найденных ошибок в коде должно только в одном случае: у компилятора вообще нет претензий к программе.
У меня это бывает только после попыток запустить отладчик.
> Более глобальный вопрос: путь к GNAT Studio в переменной PATH прописан? В каком состоянии галочка в Edit->Preferences->Jump to first Location?
Галочка есть.
> Вот такой код не только ничего выводить не должен, он даже не откомпилируется.
Я это потом понял.
> Вот чего выдало:
А как ты убрал жирную панель с картинками? С остальным я, вроде, разобрался, убрав в отдельные вкладки.
Панель с картинками - это ToolBar, там есть комбобокс "Toolbar style", перекинь его в Hide_Toolbar, панель исчезнет.
(как-то комбобоксы странно выглядят).
Да, теперь места для кода стало достаточно.
Остался только вопрос - как же внутри реализован Controlled? Что у него за поля Prev и Next? И такие имена для процедур инициализации и финализации - это вшито в язык?
О, я сумел запустить отладчик.
Понравилось, что щелчок по адресу (в структуре с указателем) раскрывает его содержимое.
Но нельзя ли показ переменных сделать попроще?
А то нередко бывает список из 20 переменых.
Размещать их мышкой по полю не лучший вариант.
Да, ещё окно дизассемблера не хочет показывать код, но мне пока страшно туда смотреть - я хоть и понимаю, что все конструкции могут развернуться и скомпилироваться без лишнего, но их высокоуровневость пугает.
> Это поля для типа Simple_List_Controller, который используется при организации списка контролируемых объектов.
То есть мне это не нужно пока, я так понял.
> Да, это вшито. А что, хочется другие имена?
Нет, просто чтоб знать. Мало ли там какой-то хитрый хак используется в описании типа, тогда интересно, как его реализовали.
Теперь мне понадобилось обойти излишне жёсткий контроль за процедурными типами.
один модуль
generic
type KeyBase is (<>);
type Value is private;
package Dictionaries is
type Dictionary is private;
type KeyArr is array (Positive range <>) of KeyBase;
type ItemFunc is access procedure(K: KeyArr; V: Value);
...
procedure EnumNodes(D: Dictionary; P: ItemFunc);
-- обойти словарь по алфавиту, для каждого узла применить P
procedure Test is
package DCI is new Dictionaries(Character, Integer);
use DCI;
procedure OutPair(S: string; i: integer) is
begin
null;
end;
D: Dictionary;
begin
EnumNodes(D, OutPair'access);
end Test;
Ну, во-первых, Ада - это не Дельфи, для того чтоб описать переменную процедурного типа, передаваемую в другую процедуру, надо делать так:
procedure EnumNodes(D: Dictionary;
P: access procedure(K: KeyArr; V: Value));
procedure OutPair(S: DCI.KeyArr; I: integer) is
begin
null;
end;
> procedure EnumNodes(D: Dictionary;
P: access procedure(K: KeyArr; V: Value));
Тут можно использовать анонимные процедурные типы в заголовке функции?!
> procedure OutPair(S: DCI.KeyArr; I: integer) is
Это проканало, только вместо Put(S) пришлось писать Put(String(S));
> Нет. А зачем?
Ну иногда в шаблоне надо инициализировать переменную неопределённого типа хоть чем-то. А FillChar типа не соответствует идеологии.
Ещё, по аналогии с Unbounded_String, нету ли в стандарте шаблонного Unbounded_Array для любого типа?
Также мне понадобились вещественные функции, умеющие работать с бесконечностями и не-числами без лишних проверок и исключений, я даже нашёл нужный модуль, но компилятор плюётся оранжевым и обещает страшные кары за его использование.
Ещё приходится извращаться с синонимами и сокращениями, чтобы не было совпадений в имени типа, имени пакета, имени переменной, константы перечислимого типа. Соглашения есть какие-нибудь, например, пакеты с буквы P, типы с буквы T (в Дельфи это удобно было), константы перечислимого типа - с заглавных букв названия типа, указатели на типа - с буквы A?
package pDemo is
type tDemo is (dDemo, dRelease, dTest);
type aDemo is access TDemo;
Demo: tDemo;
> Если ты предварительно опишешь тип, а потом будешь его использовать в заголовке (дельфийская практика), то даже если это и заработает где-то в одном случае, всегда остается вероятность, что в других случаях это работать не будет.
Тогда зачем вообще может понадобиться описывать такой тип?
Так, вспомнил.
При описании оконного класса (в задании оконной функции) проблем тут не будет?
> Вообще-то в стандарте и Ada.Containers.Ordered_Maps и Ada.Containers.Hashed_Maps есть, это ж тебя не останавливает от написания своего Dictionary?
Словарь я пишу свой, потому что:
1. Велосипеды полезны для общего развития и особенно для изучения языка.
2. Я пишу именно префиксное дерево (а не красно-чёрное дерево (Ordered_Maps) и не хеш-список (Hashed_Maps)) даже не для хранения данных по ключу (хотя у него очень высокая алгоритмическая эффективность), а для выделения самого длинного слова словаря, являющегося началом данной строки (или продолжением строки с определённой позиции).
То есть это именно словарь, а не карта.
В Дельфи меня парило, что этот словарь надо самому удалять, у меня даже были мысли делать его динмассивом, а вместо ссылок использловать номера потомков в массиве. Народ ещё рассказывал про хитрые приведения к интерфейсам (для автоудаления), я, правда, не знаю, насколько это обезопасит от мёртвых ссылок.
Ну и ещё (в Д7) я для универсальности (шаблонов-то нету) брал словарь строк из строк, а другие типы побайтово отображал в строку.
> Есть Ada.Containers.Vectors, аналог std::vector из STL: http://www.martin.dowie.btinternet.co.uk/C...rs-vectors.html
Спасибо.
pragma Convention (Stdcall, Func_Type);
Ещё вопрос - есть ли тут аналог паскалевского with?
Нет, он здесь на фиг не нужен. При наличии возможности сделать
declareпаскалевский With теряет преимущества.
R : SomeType renames Very_Long_Path_To_Rec;
begin
R.field1 := value;
-- ...
end;
Посмотрел я Container.Vector.
Не нашёл двух функций (самых главных, наверное).
Array_To_Vector
и
Vector_To_Array
Поэлементно собирать неудобно.
И в доступе к элементу много проверок. Компилятор умеет ветки, состоящие только из выброса исключения, распознавать как проверки и выкидывать в неотладочной версии?
Разделено: http://forum.pascal.net.ru/index.php?showtopic=27659 + http://forum.pascal.net.ru/index.php?showtopic=27648
Новая проблема.
Я обнаружил утечку памяти у себя. Стал искать.
Нашёл.
Суть такая.
Есть процедура P(T: out aInteger);
Где-то внутри неё происходит T := new integer;
И где-то дальше (внутри неё) какая-то проверка на корректность выражения и выброс исключения.
Внешний код примерно такой:
PF.T := null;
P(PF.T);
что-то сделать с PF.T
И в деструкторе объекта PF прописано что-то типа if T /= null then Free(T);
Так вот, если внутри P кинуть исключение, то значение переменной PF.T, передаваемой внутрь процедуры, останется старым (как будто её не трогали)! И к объекту, созданному внутри, нет доступа и он повиснет в памяти - утечка.
То есть сопоставления внутренного out-параметра и внешней переменной как бы и нету, оно неявно копируется при корректном выходе из процедуры, а при некорректном внутренний объект висит.
Что делать?
Ну допустим, в моём случае, я разберусь как-то, что в случае исключения надо освободить память, как-то разберусь в этой вложенной рекурсивной структуре, где что надо освобождать в случае утечки. Но если мне дейтвительно снаружи нужно значение указателя (или целого) даже если выкинулось исключение? Есть ли оператор, который позволил бы прямо записать в наружные параметры результат? Аналогичный вопрос про return (и все языки, где он есть) - как одновременно выкинуть исключение и вернуть значение?
А вообще мысль с копированием переменной в стек хорошая - меньше неявных разыменований указателя, оптимизация...
По-моему, у кого-то неправильное понимание сущности исключений...
Да я вообще исключениями впервые в жизни решил воспользоваться. Обычно всё на кодах возврата пишу, а исключениями просто обматываю некоторые куски после жалоб пользователей, чтобы если упадёт, то пользователю вывелось что-нибудь посодержательнее, чем "облом по адресу 87654321, обращение к 12345678".
У меня почему-то стало пропадать окно с переменными. То есть если его открыть, потом отрубить отладчик, а потом снова запустить отладчик, то окна переменных не будет видно, чтобы оно появилось, надо все окна утащить в сторону (как плавающие).
В режиме отладки вместо строк, передаваемых в процедуры, видны какие-то адреса, при попытке узнать, что по ним, показывает "неизвестная переменная". Вычисление функций в режиме отладки не работает.
Пока пытаюсь понять, почему моя программа показывает окно только в неотладочном режиме, а в отладочном не хочет регистрировать оконных класс.
Добавлено через 13 мин.
Переменная для extended return не видна в отладчике.
Странно...
http://www.youtube.com/watch?v=D-z_71O-8lo, прекрасно показывается окно переменных при инициализации/финализации дебаггера... И окно создается в режиме Debug. И строку показывает...
> Вот тут я записал небольшое видео
Дома посмотрю.
> И окно создается в режиме Debug.
Для того, чтобы преобразовать строку в PCCH при создании оконного класса, я применял функцию, создававшую временный контролируемый объект, который внутри хранил выделенную в куче строку S&0, а также поле с указателем на эту строку. В финализаторе объекта эта строка удалялась.
Если написать что-то типа
C := PCHAR(S).ref;
DoWinAPIFunc©;
То временный объект финализируется не при выходе из подпрограммы, а сразу после присваивания C := PCHAR(S).ref;
Вот и глючило. Мда, нюансов-то сколько.
> Может, все дело - в том, что у тебя Gnat GPL 2010, а у меня - GPL 2009?
Да, с окном переменных я понял, оно появляется, но иногда имеет 0ю толщину, его надо вытянуть.
Ещё проблема.
Большую часть времени написания код я не код пишу, а проблемы такие пытаюсь разгребать.
Очень тяжело, просто ужас как тяжело.
Если бы ботланд не додумался до идиотской идеи не вводить автодеструкторы в ООП, я бы давно забил на ГНАТ.
> Что я не так делаю?
Используешь старую версию?
А в 2009 точно всё в порядке? Может, откатиться попробовать.
Не знаю, мне не с чем сравнивать. Я 2010 под Windows не видел никогда. 2009 - совершенно нормальная версия. Пока не примут А2012, и не поправят под него компилятор - дома обновляться не собираюсь.