1. Пользуйтесь тегами кода. - [code] ... [/code] 2. Точно указывайте язык, название и версию компилятора (интерпретатора). 3. Название темы должно быть информативным. В описании темы указываем язык!!!
Новые вопросы - какая функция определяет наличие или отсутствие утечек памяти? Я скачал ВинАДУ, поставил, как подключить пакет, содержащий заголовки виндовых библиотек? with ADA.Windows не рабтает, нет такого пакета.
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, нужно?
Всё равно непонятно. Как его подключить к указателю, зашитому внутрь объекта из другого пакета? Пока только приходит в голову объявить его в отельном модуле, который подключить ко всем модулям проекта.
А если мне потом понадобится его отрубить, то надо написать как-то D_Pool: Standart_Pool ?
, то не надо его подключать. Это только средство отладки. Закончил отладку (убедился, что утечек нет) - отключай GNAT.Debug_Pools и все use D_Pool.
Цитата
Как его подключить к указателю, зашитому внутрь объекта из другого пакета? Пока только приходит в голову объявить его в отельном модуле, который подключить ко всем модулям проекта.
Правильно. С учетом того, что использование Use невозможно для типов, описанных в другом пакете - это единственный способ: везде, где у тебя есть динамическое выделение памяти и ее удаление (через Unchecked_Deallocation) добавить строку, указывающую пул для access-типа. Ну, а чтоб она была одна и та же - описать ее в отдельном пакете.
Вот чего в Аде нет ( напрямую нет, но можно имитировать: Conditional Compilation ) - это условной компиляции, так что когда тебе понадобится убрать отладку - придется еще раз пройти по всем пакетам, и убрать назначение для Storage_Pool.
А переопределить в том модуле 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;
(аналогичные #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, и опять пересоберешь - отладочные строки не будут компилироваться...
Разумеется. Ключ -gnateD создает символ для условной компиляции (это аналог Паскалевского ключа /D). Как назовешь - так и будет. Напиши -gnateDTestLeaks=True и будет тебе счастье...
В общем, заставить определять некорректные выражения и заставить освобождать память и для них, при возникновении исключений, оказалось непросто. Невозврат out-параметра при исключении изрядно меня напряг, заставив многое усложнить.
(проще было бы все созданные по пути деревья в отдельный список заносить, и освобождать по списку, чтобы не париться с рекурсивными выходами).
(определять конец текущего выражения тоже вопрос отдельный, короче 95% времени я не выражение разбирал, а определял некорректности, а сам разбор тривиален)
Ну, в принципе все нормально. Только непонятно, почему ты оставил вот такие повторы:
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 тоже можно напрямую заменить...
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 ("**");
Add_Func("exp", "Экспонента", new TF(Exp)'access);
Можешь, конечно, написать, только компилироваться оно не будет. Ада запрещает in-place инстанциацию дженериков. Инстанциация должна проводиться там, где допустимо описание новой подпрограммы. В выражении это недопустимо.
Хм... А чего ты вручную организуешь циклы по контейнерам, вместо того ,чтоб воспользоваться готовыми? Вот тут, например:
Цитата
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.
Тоже не помогло. Главное, я нажимаю ctrl+F9, а программа пересобирается мгновенно, будто ей не надо все модули из-за новой директивы перекомпилировывать.