Помощь - Поиск - Пользователи - Календарь
Полная версия: Отлов утечек памяти - GNAT
Форум «Всё о Паскале» > Современный Паскаль и другие языки > Ада и другие языки
Страницы: 1, 2
TarasBer
Новые вопросы - какая функция определяет наличие или отсутствие утечек памяти?
Я скачал ВинАДУ, поставил, как подключить пакет, содержащий заголовки виндовых библиотек? with ADA.Windows не рабтает, нет такого пакета.
volvo
Цитата
какая функция определяет наличие или отсутствие утечек памяти?
Целый пакет есть для этого:
with GNAT.Debug_Pools; use GNAT.Debug_Pools;

-- ... Остальные используемые пакеты

procedure Main is

D_Pool : Gnat.Debug_Pools.Debug_Pool; -- Добавляем

-- Тут идут твои функции/процедуры

begin
GNAT.Debug_Pools.Configure (D_Pool, Raise_Exceptions => False);

-- Работаешь со своими функциями/процедурами

GNAT.Debug_Pools.Print_Info_Stdout (D_Pool, Display_Leaks => True);
end Main;
После окончания работы программы получаешь полный список: сколько выделил памяти, сколько освободил, где не освободил...


Цитата
Я скачал ВинАДУ, поставил, как подключить пакет, содержащий заголовки виндовых библиотек? with ADA.Windows не рабтает, нет такого пакета.
Правильно. Его и не было.

with Win32. { тут может быть много дочерних пакетов }


Но сначала открой GPR-файл, и самой первой строкой добавь

with "win32ada";
-- Дальше - то, что и было, описание project ... is
, после чего перезагрузи проект. А еще лучше - сделать это без загруженной IDE. Я всегда так делаю - создаю новый проект, выхожу из IDE, правлю GPR, и загружаю среду заново (ну, или вообще набираю GPR вручную. Там всего - то 10 строк нужно)... Если устанавливал win32ada-gplXXXX.exe и ошибок при установке не было - то все должно завестись.

P.S. Я тут задумал написать про нововведения в A2012, нужно?
TarasBer
> Целый пакет есть для этого:

Всё по нулям выводит, якобы я ничего не выделял и не освобождал.
Total allocated bytes :  0
Total logically deallocated bytes : 0
Total physically deallocated bytes : 0
Current Water Mark: 0
High Water Mark: 0

List of not deallocated blocks:

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

> with "win32ada";
А это что за конструкция - название в кавычках? Типа заставить среду навсегда подключить набор пакетов?

> P.S. Я тут задумал написать про нововведения в A2012, нужно?
Ну вообще интересно, да.
volvo
Цитата
Типа заставить среду навсегда подключить набор пакетов?
Не навсегда, а для данного проекта.

Цитата
Всё по нулям выводит, якобы я ничего не выделял и не освобождал.
Странно... Попробовал "забыть" освободить выделенную память, меня тут же ткнули носом:
Нажмите для просмотра прикрепленного файла

А. Ну да, я забыл написать про use D_Pool... На скрине видно, как оно используется...
TarasBer
Всё равно непонятно. Как его подключить к указателю, зашитому внутрь объекта из другого пакета? Пока только приходит в голову объявить его в отельном модуле, который подключить ко всем модулям проекта.

А если мне потом понадобится его отрубить, то надо написать как-то D_Pool: Standart_Pool ?
volvo
Цитата
А если мне потом понадобится его отрубить
, то не надо его подключать. Это только средство отладки. Закончил отладку (убедился, что утечек нет) - отключай GNAT.Debug_Pools и все use D_Pool.

Цитата
Как его подключить к указателю, зашитому внутрь объекта из другого пакета? Пока только приходит в голову объявить его в отельном модуле, который подключить ко всем модулям проекта.
Правильно. С учетом того, что использование Use невозможно для типов, описанных в другом пакете - это единственный способ: везде, где у тебя есть динамическое выделение памяти и ее удаление (через Unchecked_Deallocation) добавить строку, указывающую пул для access-типа. Ну, а чтоб она была одна и та же - описать ее в отдельном пакете.

Вот чего в Аде нет ( напрямую нет, но можно имитировать: Conditional Compilation ) - это условной компиляции, так что когда тебе понадобится убрать отладку - придется еще раз пройти по всем пакетам, и убрать назначение для Storage_Pool.

Можно также воспользоваться The gnatmem tool
TarasBer
> Закончил отладку (убедился, что утечек нет) - отключай GNAT.Debug_Pools и все use D_Pool.

Жесть какая... А переопределить в том модуле D_Pool на стандартный можно, это какая конструкция делает?

> Вот чего в Аде нет - это условной компиляции

А хоть введут? Блоки вида if true then... - это покрывает только один случай применения условной компиляции.
volvo
Цитата
А переопределить в том модуле D_Pool на стандартный можно, это какая конструкция делает?
Можно, но это плохая идея. Дело в том, что глобальные ссылочные типы используют один пул (из пакета System.Pool_Global), локальные - другой (из System.Pool_Local). Что, будешь переопределять всё в один? Я бы не делал этого.

Цитата
А хоть введут? Блоки вида if true then... - это покрывает только один случай применения условной компиляции.
Ну я ж написал, что нет в явном виде. Используй gnatprep - это несложно:

#if DEBUGGING
with GNAT.Debug_Pools; use GNAT.Debug_Pools;
#end if;

with Ada.Unchecked_Deallocation;

with Ada.Text_IO;
with My_Pool;
with MyObj;

procedure Main is

type P_Integer is access Integer;

#if DEBUGGING
for P_Integer'Storage_Pool use My_Pool.D_Pool;
#end if;

procedure Free is new Ada.Unchecked_Deallocation (Integer, P_Integer);

procedure Test is
A, B : P_Integer;
begin
A := new Integer;
B := new Integer;

A.all := 10;
B.all := 20;
Free (A);
end Test;

begin
#if DEBUGGING
GNAT.Debug_Pools.Configure (My_Pool.D_Pool, Raise_Exceptions => False);
#end if;

Test;
MyObj.Test_Recs;

#if DEBUGGING
GNAT.Debug_Pools.Print_Info_Stdout (My_Pool.D_Pool, Display_Leaks => True);
#end if;
end Main;
(аналогичные #if/#end if; во всех файлах, где добавляется тестирование), потом открываешь GPR-файл, и ищешь там описание пакета Compiler. У меня оно, скажем, выглядит так:
   package Compiler is
for Default_Switches ("ada") use ("-gnatVn", "-gnata", "-g", "-gnatf", "-gnat05");
end Compiler;


добавляешь сюда же, в дефолт-опции, еще и предварительную обработку процессором:
   package Compiler is
for Default_Switches ("ada") use ("-gnatVn", "-gnata", "-g", "-gnatf", "-gnat05") & ("-gnateDDEBUGGING=True");
end Compiler;


Сохраняешь файл и полностью пересобираешь проект. Когда отладишь - снова зайдешь сюда, поменяешь в определении ключа True на False, и опять пересоберешь - отладочные строки не будут компилироваться...
TarasBer
> ("-gnateDDEBUGGING=True")

А для этих ключей можно свои имена придумывать? Например "-TestLeaks=True"?
volvo
Разумеется. Ключ -gnateD создает символ для условной компиляции (это аналог Паскалевского ключа /D). Как назовешь - так и будет. Напиши -gnateDTestLeaks=True и будет тебе счастье...
TarasBer
В общем, заставить определять некорректные выражения и заставить освобождать память и для них, при возникновении исключений, оказалось непросто. Невозврат out-параметра при исключении изрядно меня напряг, заставив многое усложнить.

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

Новая версия:

Нажмите для просмотра прикрепленного файла

добавлена фича - если записать в ряд несколько выражений, то считаются все:
Код

Input expression or "exit": sin(pi/3) cos(pi/3)
sin(pi/3) = 8.66025403784438647E-01
cos(pi/3) = 5.00000000000000000E-01

(определять конец текущего выражения тоже вопрос отдельный, короче 95% времени я не выражение разбирал, а определял некорректности, а сам разбор тривиален)
volvo
Ну, в принципе все нормально. Только непонятно, почему ты оставил вот такие повторы:

   function Div(Args: Vector) return F80 is
begin
if integer(Length(Args)) /= 2 then raise Invalid_Expression;
end if;
return Get_Value(Element(Args, 0)) / Get_Value(Element(Args, 1));
end;

function Pow(Args: Vector) return F80 is
begin
if integer(Length(Args)) /= 2 then raise Invalid_Expression;
end if;
return Get_Value(Element(Args, 0)) ** Get_Value(Element(Args, 1));
end;

? То же самое касается и умножения/сложения всех элементов вектора. Это ж одинаковые действия. Зачем дженерики тогда? Делаем так:
   generic with function F(Left, Right : F80) return F80;
function TwoParams (Args : Vector) return F80;

function TwoParams (Args : Vector) return F80 is
begin
if integer(Length(Args)) /= 2 then raise Invalid_Expression;
end if;
return F(Get_Value(Element(Args, 0)), Get_Value(Element(Args, 1)));
end TwoParams;

function Div(Args: Vector) return F80 is
function MyFunc is new TwoParams ("/");
begin
return MyFunc(Args);
end;

function Pow(Args: Vector) return F80 is
function MyFunc is new TwoParams ("**");
begin
return MyFunc(Args);
end;

Еще не совсем понял, почему в Lib_Add_Funcs (при добавлении поддерживаемых функций) ты добавляешь Summ'access, вместо того, чтобы напрямую добавить Add'Access? Зачем вообще этот переходник Summ нужен? Закомментировал его, вроде и без него все нормально. Или я чего-то не увидел? Prod -> Mul тоже можно напрямую заменить...
TarasBer
Summ и Prod пока действительно не нужны. Это я добавил чтобы было отдельно для оператора и отдельно для функции. Ну мало ли в будущем мне понадобится ещё какой-нибудь контроль, и в Add (для оператора) я допишу "аргументов должно быть ровно два". Правда, тогда придётся наоборот вызывать Summ из Add.

Кстати, я вот хотел вместо

function Exp is new TF(Exp);
Add_Func("exp", "Экспонента", Exp'access);

написать

Add_Func("exp", "Экспонента", new TF(Exp)'access);

Мне кажется, что это что-то уже близкое к лямбдам.

> Зачем дженерики тогда? Делаем так:

Для функций я догадался до генериков.
Для операторов - нет, исправлю.
Кстати, для них можно же написать сразу

function Div is new TwoParams ("/");
function Pow is new TwoParams ("**");

?
volvo
Цитата
Кстати, для них можно же написать сразу
Ну да, и так тоже можно...

Цитата
Add_Func("exp", "Экспонента", new TF(Exp)'access);
Можешь, конечно, написать, только компилироваться оно не будет. Ада запрещает in-place инстанциацию дженериков. Инстанциация должна проводиться там, где допустимо описание новой подпрограммы. В выражении это недопустимо.
TarasBer
> Можешь, конечно, написать, только компилироваться оно не будет.

Вот и я о том же. А ведь если б разрешили, то такое заворачивать можно было бы!
volvo
Хм... А чего ты вручную организуешь циклы по контейнерам, вместо того ,чтоб воспользоваться готовыми? Вот тут, например:

Цитата
   procedure Del_Tree(T: in out aFunc_Tree) is

procedure Del_Childs(v: Func_Tree_Vectors.Vector) is
use Func_Tree_Vectors;
T: aFunc_Tree;
begin
for i in First_Index(v) .. Last_Index(v) loop
T := Element(v, i);
Del_Tree(T);
end loop;
end;

begin
if T /= null then
case T.Kind is
when Operator | Func => Del_Childs(T.Childs);
when others => null;
end case;
Free(T);
T := null;
end if;
end;

Да и еще я где-то видел цикл по всему контейнеру... Можно же сделать так:
   procedure Del_Tree(T: in out aFunc_Tree) is

procedure Del_Childs(C : Func_Tree_Vectors.Cursor) is
T : aFunc_Tree := Element ( C );
begin
Del_Tree (T);
end Del_Childs;

begin
if T /= null then
case T.Kind is
when Operator | Func => T.Childs.Iterate (Del_Childs'Access);
when others => null;
end case;
Free(T);
T := null; -- Это можно не делать
end if;
end;

Почему T := null можно не делать? Это будет гарантированно сделано в Unchecked_Deallocation:

Цитата(Ada RM 13.11.2)
Given an instance of Unchecked_Deallocation declared as follows:
procedure Free is new Ada.Unchecked_Deallocation(object_subtype_name, 
access_to_variable_subtype_name);

Procedure Free has the following effect:
1. After executing Free(X), the value of X is null.
2. Free(X), when X is already equal to null, has no effect.
-TarasBer-
> Да и еще я где-то видел цикл по всему контейнеру...

Кажется в том месте, где я перегонял массив в вектор? Я ещё спрашивал, нет ли готового метода.
Ну и ещё в Adjust цикл по контейнеру для копирования.

> Можно же сделать так:

Ну буду знать.
TarasBer
А как сделать так, чтобы код между

#if Test_Leaks

и

#end if;

не выполнялся?

Я убрал
& ("-gnateDTest_Leaks=True")
, не помогло. Закрыл среду, удалил exe, открыл среду, всё равно не помогло.
volvo
& ("-gnateDTest_Leaks=False")
TarasBer
Тоже не помогло. Главное, я нажимаю ctrl+F9, а программа пересобирается мгновенно, будто ей не надо все модули из-за новой директивы перекомпилировывать.
volvo
После того, как внес изменения (если делал это через IDE) Project -> Save All делал? В настройках проекта Recompile if switches changed стоит? Clean All (в самом крайнем случае) для удаления всех библиотек, и потом пересборка проекта - помогает?
TarasBer
> В настройках проекта Recompile if switches changed стоит?

Поставил, заработало.

Я видел эту галочку, думал, что это как самоочевидное должно быть, поэтому понял её, как "перекомпилировывать только если что-то там менялось", для надёжности я её ставить не стал, потому что помню, как в Дельфи7 я компилировал прогу с какими-то галочками, а потом меня спрашивали - а что это программа выкидывает окно при ненайденном файле? А я смотрю - не стоит галка на проверках ввода-вывода. Что такое, думаю, за бред? А оказалось, что чтобы галочки заработали, надо там вообще всё пересобирать. Вот и не доверяю я частичной пересборке.
Кстати, полная сборка там была на порядок быстрее.
volvo
Ну, там например не было препроцессора. А здесь мало того, что у тебя надо файлы компилировать, так еще и препроцессором проходить теперь надо - это тоже время. Плюс ко всему, скорость сборки зависит от режима (дебаг это или оптимизация, или профайлер). Да и возможностей у языка больше. Но все равно, скорость сборки у Ады на порядки выше С++-ной...
TarasBer
> Ну, там например не было препроцессора.

В таком виде он там был.
{$IFDEF} {$ENDIF} я пользовался по полной.
Или тут что-то ещё есть?

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

Быстрее всего - неоптимизированный неотладочный режим?

> Да и возможностей у языка больше.

Ну понятно, что шаблоны раскрыть время нужно, хотя с чего бы это, там же не на уровне текста подстановка делается, да и каждый шаблонный пакет у меня в единственном экземпляре инициализируется (кроме вектора). Но даже простой код без фич собирается заметно дольше.
volvo
Цитата
В таком виде он там был.
{$IFDEF} {$ENDIF} я пользовался по полной.
Это не препроцессор. Вот когда перед компиляцией файла он скармливается внешней программе, которая из него убирает всё ненужное, руководствуясь известными ей директивами и параметрами, переданными в нее - вот это препроцессор. То, что было в Паскаль/Дельфи - это условная компиляция. Причем самая примитивная. ifdef/ifndef/ifopt/else/endif - это все, что есть в Дельфи. А комбинировать условия через Or/And? Я ж могу захотеть и задать несколько ключей, а потом написать

#if Debugging and Test_Leaks
-- ...
#end if;

А получить строковое представление ключа в рантайме? Вот откомпилировал я программу с
& ("-gnateDMyStr=""Test""");
, и хочу вывести строку, переданную в MyStr на печать. Как такое сделать в Дельфи? Я-то вот так сделаю:

s : String := $MyStr;

, и все, обычная строка, что хочешь с ней - то и твори...

Цитата
Быстрее всего - неоптимизированный неотладочный режим?
Да, default в комбобоксе режимов. Хотя у меня есть ощущение, что под Windows этот комбобокс как-то не так работает. Вообще он должен при переключении менять в настройках проекта некоторые свойства, он этого не делает, насколько я помню. Приходится вручную лазить в свойства проекта, и там устанавливать самостоятельно. Хорошо, что хоть компилирует в разные папки...
TarasBer
> А комбинировать условия через Or/And?

or:

{$IFDEF COND1}
{$DEFINE COND3}
{$ENDIF}

{$IFDEF COND2}
{$DEFINE COND3}
{$ENDIF}

and:

{$IFDEF COND1}
{$IFDEF COND2}
{$DEFINE COND3}
{$ENDIF}
{$ENDIF}

> А получить строковое представление ключа в рантайме?

Зачем это?
const S: string = 'test';

volvo
Цитата
Зачем это?
Затем, что не всегда допустимо править исходники. А иногда - просто невозможно обойтись другими средствами. Вот если я хочу в программу запихать дату и время ее сборки (или характеристики машины, на которой производилась сборка) - твои действия?
TarasBer
> Вот если я хочу в программу запихать дату и время ее сборки

А разве константа, определённая через функцию GetTime, не превратится во время сборки (ведь константы считаются компилятором, а не программой при старте)?
Аналогично про характеристики - константу определить через GetHardSerialNumber.

> Но все равно, скорость сборки у Ады на порядки выше С++-ной...

С++ плохой пример по скорости сборки, не надо с ним сравнивать.


По поводу самой функции Do_Tree возражений нету, кстати?
volvo
Цитата
А разве константа, определённая через функцию GetTime, не превратится во время сборки (ведь константы считаются компилятором, а не программой при старте)?
Ага, щаззз...
with ada.Calendar;
with ada.Calendar.Formatting;
...
Time : constant Ada.Calendar.Time := Ada.Calendar.Clock; -- Перед Main-ом
...
Ada.Text_IO.Put_Line (Item => Ada.Calendar.Formatting.Image (Time) ); -- Проверяем
А теперь собираем программу и запускаем EXE-шник несколько раз с интервалом в несколько десятков секунд. Что получается? Получается, что текущее время берется при старте программы, а вот потом изменить его программа не даст, ибо constant. Так как быть, если мне надо время/конфиг именно при сборке? Я поступаю просто донельзя: есть скрипт, собирающий в строку всю нужную мне информацию, эта строка передается в gnatprep, и строка жестко зашивается в EXE-шник...
TarasBer
То есть константы считаются не компилятором, а программой при старте?
Это как-нибудь настраивается? Или считается, что скорость старта программы не важна?
volvo
Цитата
То есть константы считаются не компилятором, а программой при старте?
Ada.Calendar.Clock - это все-таки функция. То есть, ты хочешь, чтобы при компиляции еще и вызывались функции, и результат их работы запоминался как обычная константа? Этого не было и не будет никогда.

Для простых типов данных - да, компилятор в состоянии описать настоящую константу. Для сложных - нет. Как я могу, скажем, получить истинную константу для Unbounded_String, если мне для этого надо вызвать процедуру Initialize? Только на этапе запуска программы... А вот для Ch : constant Character := 'A'; проблем не будет - для символов ничего не надо выполнять, компилятор подставит сразу нужное значение, еще в compile-time...
volvo
Цитата
По поводу самой функции Do_Tree возражений нету, кстати?
Я бы немного "разгрузил" функцию от множества if/end if. То есть, вынес бы выброс исключений в отдельные функции (там всего два вида выбрасывается, так что всего 2 доп. функции - Check_IA и Check_BB), по проверке условия. Один фиг, когда ты получаешь исключение - получаешь и Call stack traceback locations, которые можно преобразовать в названия процедур:

Нажмите для просмотра прикрепленного файла

Ну, и вот так я б не написал:
Цитата
            while Was_Token loop
-- пока у нового оператора приоритет выше, чем у текущего
case Last_Token.Kind is
when Operator =>
if Less(KP, To_Pair(Last_Token), Last_Token.Right_Associated) then
High_Level := True; -- повышаем уровень
Do_Tree(T, To_Pair(Last_Token));
else
exit;
end if;
when Close_Bracket | Delimiter => exit;
when others =>
Position.all := Old_Position;
Was_Token := False;
return;
end case;
end loop;
, лишние if/else, я б сделал так:
            while Was_Token loop
-- пока у нового оператора приоритет выше, чем у текущего
case Last_Token.Kind is
when Operator =>
exit when not Less(KP, To_Pair(Last_Token), Last_Token.Right_Associated);
High_Level := True; -- повышаем уровень
Do_Tree(T, To_Pair(Last_Token));
when Close_Bracket | Delimiter => exit;
when others =>
Position.all := Old_Position;
Was_Token := False;
return;
end case;
end loop;
TarasBer
Ну это тоже можно.
Гораздо хуже, что операторы и функции слиты в одну ветку, это не нужно, просто была задумка, чтобы каждый оператор мог быть и функцией, но я решил для + и * сделать отдельные функции.
Вопрос такой: как переписать через Iterate процедуры To_Vector и Copy_Childs, чтобы выглядело не хуже, чем было?
Ещё в выражении не надо писать в конце =, просто парсер молча останавливается, когда видит что-то совсем незнакомое, а текущее выражение корректно обработано.
volvo
А что мне надо сделать (какую строку входную заставить обработать), чтоб вызвалось Copy_Childs?
TarasBer
В данном случае оно не вызывается, потому что программа нигде не копирует функции. Но мало ли, понадобится, делать limited я не захотел.
volvo
Ну, тогда без проверки:

   function To_Vector (A : Key_Arr) return Key_Based_Vectors.Vector is

V : Key_Based_Vectors.Vector;
Arr_Index : Integer := A'First;

procedure Update (Item : in out Key_Base) is
begin
Item := A (Arr_Index);
Arr_Index := Arr_Index + 1;
end Update;

procedure Copy (C : Key_Based_Vectors.Cursor) is
begin
V.Update_Element (C, Update'Access);
end Copy;

use Key_Based_Vectors;
begin
Set_Length (V, A'Length);
V.Iterate (Copy'Access);
return V;
end To_Vector;
(почему использован Update_Element - ибо не может привести к реаллокации данных ни при каких условиях, следовательно индексация не собьется. Replace_Element от этого не застрахован).

И
	 function Copy_Childs (V : Vector) return Vector is

New_Vector : Vector;

procedure Deep_Copy (C : Func_Tree_Vectors.Cursor) is
begin
New_Vector.Replace_Element (C, Copy_Tree (Element (V, To_Index ( C ))));
end Deep_Copy;

begin
Set_Length (New_Vector, Length (V));
New_Vector.Iterate (Deep_Copy'Access);
return New_Vector;
end Copy_Childs;
TarasBer
Я так и не понял механизм контролируемых типов. Что именно в описании типа говорит компилятору, что тип контролируем?
Даже если дословно, буква в букву, переписать описание типа Controlled в другой модуль, то для него уже не работает автовызов Initialize и Finalize.
Надо, чтобы объект был именно унаследован от типа, называемого именно так-то, находящегося именно в модуле таком-то?

Ещё вопрос: почему ссылка на строку - это 64 бита? Указатель на начало и конец строки? Или указатель на начало и на длину?
Как хранятся дин массивы в памяти? Выделяются на стеке? Где хранятся их границы? Если выделяются на стеке, то где выделяются локальные переменные, объявленные после них, как хранятся ссылки на локальные переменные, объявленные до них?
volvo
Цитата
Даже если дословно, буква в букву, переписать описание типа Controlled в другой модуль, то для него уже не работает автовызов Initialize и Finalize
Если дословно (буква в букву) переписать все, что нужно в другой модуль - то он у тебя даже не откомпилируется. Потому как
gnatmake -ws -c -u -P/home/forum_test/forum.gpr my_con.ads --subdirs=debug -cargs -g -O0
gcc -c -g -gnatVa -gnatw.e -gnata -g -gnatf -gnat05 -g -O0 -I- -gnatA /home/forum_test/my_con.ads
my_con.ads:3:06: warning: "system.finalization_root" is an internal GNAT unit
my_con.ads:3:06: warning: use of this unit is non-portable and version-dependent
my_con.ads:3:06: cannot depend on "Finalization_Root" (wrong categorization)
my_con.ads:21:24: warning: declaration of "=" hides one at line 19
my_con.ads:21:24: warning: declaration of "=" hides one in package Standard
gnatmake: "/home/forum_test/my_con.ads" compilation error

Finalization_Root - это внутренний пакет, нельзя его использовать в своих целях. Это жестко зашито в GNAT.

Цитата
Ещё вопрос: почему ссылка на строку - это 64 бита? Указатель на начало и конец строки? Или указатель на начало и на длину?
Смотри. Во-первых, это только в GNAT-компиляторе. Во всех остальных для access-типов всегда используются только thin-pointers (ага, худые). В GNAT-е же для неограниченных типов (массивы, строки, без жесткого указания длины, всякие Unbounded_String-и) используется "fat pointer" - это два указателя: один - на данные, другой - на границы массива. Но это, насколько я помню, отключаемо. Можно заставить компилятор всегда использовать thin-pointers, но это может сказаться на производительности.

Цитата
Как хранятся дин массивы в памяти? Выделяются на стеке?
Динамические - это какие? Вот эти:
procedure P(N : Integer) is
arr : array (1 .. N) of Integer; -- <--- ???
begin
-- ...
end P;
? Или которые через new выделяются?
-TarasBer-
> Это жестко зашито в GNAT.

То есть работает именно с типом, унаследованного именно от Controlled из именно этого пакета?

> Динамические - это какие?

Динамические - это которые в declare пишутся.


i := 5;
...
declare
a: array (1..i) of integer;
... j := 5;
declare
b: array(1..j) of integer;


...итд

В голове только одна идея - сначала вычисляется, где на стеке будут лежать переменные с известным размером (и указатели на массивы), и ставятся подряд, а массивы аллоцируются на стеке (при этом локальные переменные известного размера и указатели на массивы адресуются относительно esp, а ebp сдвигается за массив), и так как их расположение уже не предсказать на этапе компиляции, обращение к ним идёт через указатель.
volvo
Хм. Ну, разницы с тем, что я привел - никакой. И там и там на этапе компиляции размер определить нельзя, поэтому компилятор использует одинаковую стратегию. Почитать об этом можно в книге Michael L. Scott "Programming Language Pragmatics" (перевода не видел, к сожалению).

Вот тут есть кусками: Как раз на нужной странице, если надо полную версию - скажи, я положу куда-нибудь, там около 8 Мб...
-TarasBer-
А, ну то есть ровно то, что я и сказал, судя по картинке.
Для тех переменных, для которых можно предсказать (для всех, упоминаемых во всех вложенных блоках данной процедуры), выделяем место как обычно, для тех, для которых нельзя, выделяем указатель как обычно, а их самих выделяем на вершине стека.
Это хорошо, что там не дёргается куча, всё собирается простыми алгоритмами прямо на стеке.
TarasBer
Ещё вопрос.
Контролируемые объекты, созданные, как промежуточный результат вычислений, удаляются уже после вычислений же?



procedure Test is

type aString is access all String;
type aChar is access all Character;

for aString'Storage_Pool use Debug_Options.D_Pool;

type cStr is new Ada.Finalization.Controlled with record
r: aString;
end record;

procedure Initialize(a: in out cStr);
procedure Finalize(a: in out cStr);

procedure Initialize(a: in out cStr) is
begin
a.r := null;
end;

procedure Finalize(a: in out cStr) is
procedure Free is new ADA.Unchecked_Deallocation(String, aString);
begin
if a.r /= null then
Free(a.r);
end if;
end;
--------------------------------------------------------------------------------
function To_CStr (S: String) return cStr is
result: cStr;
begin
result.r := new String(1 .. S'Length);
result.r.all := S;
return result;
end;

begin
#if Test_Leaks
GNAT.Debug_Pools.Configure (D_Pool, Raise_Exceptions => False);
#end if;

Put_Line(To_CStr("TEST").r.all);

#if Test_Leaks
GNAT.Debug_Pools.Print_Info_Stdout (D_Pool, Display_Leaks => True);
#end if;
end Test;




Выдаёт
C:\Program\ Files\ADA\projects\test\test
error: Accessing deallocated storage, at 0x6A3079AF 0x6A2CBF61 0x6A2CDC15 0x0041A416 0x0040178C 0x004010B4 0x00401146 0x7C817065
First deallocation at 0x6A3079AF 0x6A2CC522 0x6A2CE042 0x0041ABB2 0x6A31EEB8 0x0041AB0B 0x0041AA9B 0x0041A262 0x0040178C 0x004010B4 0x00401146 0x7C817065
Initial allocation at 0x6A3079AF 0x6A2CC522 0x6A2CEB62 0x0041A78E 0x0041A262 0x0040178C 0x004010B4 0x00401146 0x7C817065
TEST
error: Freeing already deallocated storage, at 0x6A3079AF 0x6A2CBF61 0x6A2CE4EB 0x0041ABB2 0x6A31EEB8 0x0041A53E 0x0041A44E 0x0040178C 0x004010B4 0x00401146 0x7C817065
Memory already deallocated at 0x6A3079AF 0x6A2CC522 0x6A2CE042 0x0041ABB2 0x6A31EEB8 0x0041AB0B 0x0041AA9B 0x0041A262 0x0040178C 0x004010B4 0x00401146 0x7C817065
Memory was allocated at 0x6A3079AF 0x6A2CC522 0x6A2CEB62 0x0041A78E 0x0041A262 0x0040178C 0x004010B4 0x00401146 0x7C817065
Total allocated bytes : 12
Total logically deallocated bytes : 12
Total physically deallocated bytes : 0
Current Water Mark: 0
High Water Mark: 12


List of not deallocated blocks:
[2011-02-22 11:15:27] process terminated successfully (elapsed time: 00.18s)


Видимо проблема в том, что return объект делает finalize и adjust. (зачем?)


Кстати, как сделать, чтобы инициализация отладчика не перекидывала меня с той страницы, что я смотрю?


Добавлено через 13 мин.
Попытка следующая. Чтобы не копировать строку лишний раз, лепим счётчик ссылок.


procedure Test is

type Cnt_Str(i: integer) is record
rc: integer;
s: string(1 .. i);
end record;

type aCnt_Str is access all Cnt_Str;
type aChar is access all Character;

for aCnt_Str'Storage_Pool use Debug_Options.D_Pool;

type cStr is new Ada.Finalization.Controlled with record
r: aCnt_Str;
end record;

procedure Initialize(a: in out cStr);
procedure Finalize(a: in out cStr);
procedure Adjust(a: in out cStr);

procedure Initialize(a: in out cStr) is
begin
a.r := null;
end;

procedure Finalize(a: in out cStr) is
procedure Free is new ADA.Unchecked_Deallocation(Cnt_Str, aCnt_Str);
begin
if (a.r /= null) then
a.r.rc := a.r.rc - 1;
if a.r.rc = 0 then
Free(a.r);
end if;
end if;
end;

procedure Adjust(a: in out cStr) is
begin
a.r.rc := a.r.rc + 1;
end;
--------------------------------------------------------------------------------
function To_CStr (S: String) return cStr is
result: cStr;
begin
result.r := new Cnt_Str(S'Length);
result.r.rc := 1;
result.r.s := S;
return result;
end;

begin
#if Test_Leaks
GNAT.Debug_Pools.Configure (D_Pool, Raise_Exceptions => False);
#end if;

Put_Line(To_CStr("TEST").r.s);

#if Test_Leaks
GNAT.Debug_Pools.Print_Info_Stdout (D_Pool, Display_Leaks => True);
#end if;
end Test;



Программа вылетает на
result.r := new Cnt_Str(S'Length);

Добавлено через 9 мин.
А на самом деле я хочу, чтобы можно было писать хотя бы так:

CreateWindow(..., TO_WIDE(ClassName).s, TO_WIDE(Title).s, ....)

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

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

А по поводу return ов - паскалевская идея с переменной, куда пишется результат, мне нравится больше, потому что сразу всё пишется в результат, без лишнего копирования.
volvo
Во-первых, не надо делать лишних вещей, Ада позволяет гораздо более красивую реализацию функции:
   function To_CStr (S: String) return CStr is
begin
return CStr'(Ada.Finalization.Controlled with r => new String'(S));
end;

, а во-вторых, не все то, что Debug_Pool показывает как утечку, на самом деле является таковой, в некоторых случаях этот метод дает осечки. valgrind не нашел утечки (с новой функцией), хотя Debug_Pool по прежнему пишет то же самое...

Можешь почитать вот это, кстати:
Куда девалась память (часть 1)
Куда девалась память (часть 2)
Куда девалась память (часть 3)


Добавлено через 18 мин.
Цитата
паскалевская идея с переменной, куда пишется результат, мне нравится больше, потому что сразу всё пишется в результат, без лишнего копирования.
То есть, Extended return, как я понимаю, тоже прошел мимо тебя?

Добавлено через 18 мин.
Цитата
А на самом деле я хочу, чтобы можно было писать хотя бы так:
CreateWindow(..., TO_WIDE(ClassName).s, TO_WIDE(Title).s, ....)
Я обычно делал так:
Ну, скажем, надо создать кнопку с кириллицей... (Показать/Скрыть)
TarasBer
> Куда девалась память (часть 1)
> Куда девалась память (часть 2)
> Куда девалась память (часть 3)
> Extended return

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

> В отдельном файле создавались все текстовые константы, файл сохранялся в формате UTF-8 стандартным виндовым блокнотом, и не редактировался редактором из GPS, отображать этот файл среда под Windows умеет, а вот сохранять его - нет, сбивается кодировка.

А они это исправлять будут? И кодировку, и то, что под виндой переносы строк надо записывать как #13#10, а не просто #13?
volvo
Цитата
А они это исправлять будут?
Я сомневаюсь, что это вообще исправимо. Где-то на AdaCore видел много вопросов "что за дела с кодировкой?", и на них отвечали разработчики среды. Пока не получается поправить (тем более что подавляющее большинство разработок на Аде ведется под никсами), там мало того, что в качестве прослойки Python воткнули, который тоже свою долю в коверкание кодировок вносит, так еще в винде с кодировками вообще - гораздо хуже, чем в никсах. Кстати, это проблема не только GPS, у меня и в QT Designer-е такая же фигня происходит: вроде заявляли полную поддержку Юникода, но как только я пишу что-то на форму - все прекрасно, как дело касается инициализации строки в исходнике - получаю кракозябры, хотя все в UTF-8.

В Дебиане нет проблем ни там ни там почему-то.
TarasBer
> тем более что подавляющее большинство разработок на Аде ведется под никсами

Это всё объясняет...

sites.google.com у меня не открывается, я погуглил по фразам из твоей подписи, попал сюда:
http://vlady.uzelok.net/smf/index.php?board=24.0

Всё тут есть?
volvo
Цитата
Всё тут есть?

Да, пока я оставил и там и там, сервисы Гугла были какое-то время недоступны, пришлось продублировать...
TarasBer
Расширенный возврат - мощная вещь, только зачем надо обязательно указывать тип?

function GetI: integer is begin
return i do // ошибка компиляции
i := 3;
end return;
end;

volvo
Потому что не всегда можно обойтись без описания типа. Как вариант:
function St return String is
begin
return Value do
-- Какого размера должна быть строка Value? Как ее заполнить?
end return;
end St;
Также с 'Class-типами могут начаться проблемы, если не указывать явно тип в расширенном return-е.
TarasBer
> return Value do

Ну например return Value(1 .. 5) do

> Также с 'Class-типами могут начаться проблемы, если не указывать явно тип в расширенном return-е.

Тогда понятно.

> В отдельном файле создавались все текстовые константы, файл сохранялся в формате UTF-8 стандартным виндовым блокнотом,

Как раз тогда лучше в ANSI из блокнота сохранить, чтобы воспринимать русское 'а' как один символ.
Ещё можно в редакторе выставить кодировку, в которой русские символы занимают 1 байт. Но там есть только KOI-8 и ISO-8859-5, ANSI нету.
Кстати, какие параметры надо прописать в MultiByteToWideChar, чтобы она работала с KOI-8 или ISO-8859-5?

А, всё оказалось проще. Я работаю с УТФ-8, но для всех процедур принимаю именно просто-строку (не юникод), которую для юникодных компонентов переделываю через MultiByteToWideChar с константой CP_UTF8 : constant WORD := 65001, скопированной из System.Win32. Кстати, почему этот модуль нельзя подключать, в нём же много полезного?
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.