Is deallocate() necessary before using new in VHDL? - memory-leaks

When using new for an access type in VHDL, must I deallocate() before using new?
I understand you need to use deallocate() to prevent leaking when you are done with the access type, I'm just not sure whether using it before new is redundant or needed.
e.g.
testAccess : process
type string_ptr is access string;
variable tmpString : string_ptr;
variable loopNum : natural := 0;
begin
tmpString := new string'("Test Loop: " & to_string(loopNum));
report "String = " & tmpString.all;
deallocate(tmpString); --is this neccessary?
tmpString := new string'("New String Usage");
report "String = " & tmpString.all;
loopNum := loopNum + 1;
deallocate(tmpString); --is this neccessary? (assuming we always loop back to first new)
wait for 1 us;
end process;

Related

Storing and using strings of varying length (ADA)

Im working on a problem where I need to make a set of boxes according to an input number where each and every box has a unique name. I've managed to create the boxes but I've only managed to insert one name on all of them as my names are overwritten in the name collecting procedure.
here is the code https://pastebin.com/FBMvvrn4
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure exercise is
N : Integer;
Names : String(1..10);
L : Integer;
procedure Objectcatcha (N: out Integer) is
begin
Put("Enter amount of objects: ");
Get(N);
end Objectcatcha;
procedure Namescatcha (Names: out string; L: out integer) is
begin
for I in 1..N loop
Get_Line(Names, L);
end loop;
end Namescatcha;
procedure SpaceBox(Names: in String; L: in Integer; N : in integer) is
begin
for I in 1..N loop
Put("+-----------+ ");
end loop;
New_Line;
for I in 1..N loop
Put("! ");
Put(Names(1..L));
for J in (L+1)..10 loop
Put(" ");
end loop;
Put("!");
if I = N then
Put("");
else
Put("<>---");
end if;
end loop;
New_Line;
for I in 1..N loop
Put("+-----------+ ");
end loop;
end SpaceBox;
begin
Objectcatcha(N);
Put("Enter the name of the objects: ");
Namescatcha(Names, L);
SpaceBox(Names,L, N);
end exercise;
I've been sitting around a lot with this and Id be very glad if someone could help me find a way to name each box individually.
Thanks in advance!
Where you can, (and you can here), just declare a variable of the exact size to hold the name you are using. This can be done by declaring it as an indefinite array, and initialising it with the correct name.
So your main program could be:
Objectcatcha(N);
For I in 1 to N loop
Put("Enter the name of the next object: ");
Declare
Name : String := Namescatcha;
Begin
SpaceBox(Name, Name'Length, N);
End;
End loop;
Namescatcha is now a function returning just the right size of string:
function Namescatcha return String is
begin
return Getline;
end Namescatcha;
and you should probably rewrite Spacebox without L (you can always use Name'Length to see the length of Name)
Brian Drummond already covered how to get back a variable length name and some means to work with them. In order to address your one name overwriting all the names problem, you have to consider that you are using one name variable to hold them all so it makes sense that one is overwriting the others. To store multiple names together, consider using an Indefinite_Vector to hold them. In your Objectcatcha procedure you get the capacity so use that to set the size of the vector
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Indefinite_Vectors; use Ada.Containers;
-- Other stuff
package Name_Vectors is new Indefinite_Vectors
(Index_Type => Positive,
Element_Type => String);
Names : Name_Vectors.Vector;
package Count_Type_IO is new Integer_IO(Count_Type);
procedure Objectcatcha is
Number : Count_Type;
Last : Positive; -- Placeholder for Get call
begin
Put("Enter amount of objects: ");
Count_Type_IO.Get
(From => Get_Line,
Item => Number,
Last => Last);
Names.Reserve_Capacity(Number);
end Objectcatcha;
procedure Namescatcha is
begin
for Index in 1..Names.Capacity loop
Names.Append(Get_Line);
end loop;
end Namescatcha;
You will need to adjust your SpaceBox procedure to use the vector instead of the name or to only do one name at a time (your choice).
A few notes:
1. I changed your Get call to Get_Line for getting the number of names, you can change it back if you want.
2. When I stored the names in the vector, the last character stored might be the "new line" character, so you may have to strip it out. That's easy to do. Just use all the characters of the name except the last one. for example:
EDIT: Simon Wright indicated that this shouldn't be needed. My implementation does, so I'll leave this here if you have a setup similar to what I tested on and the new lines are copied.
declare
Name : String := Names(Index);
begin
Put(Name(1..Name'Length-1));
end;
Since your program seems to be a non-performance-critical application, I'd use variable-size strings to avoid storing the N different string lengths.
In plain Ada variable-size strings are called Unbounded_String.
Here, your exercise using an open-source package (hac_pack: spec, body) which facilitates things around variable-size strings.
with HAC_Pack; use HAC_Pack;
procedure Names_in_Boxes is
Max : constant := 100;
type Names_Type is array (1 .. Max) of VString;
procedure Objectcatcha (N: out Integer) is
begin
Put("Enter amount of objects: ");
Get(N);
Skip_Line;
end Objectcatcha;
procedure Namescatcha (Names: out Names_Type; N : in Integer) is
begin
for I in 1..N loop
Put(+"Object " & I & ": ");
Get_Line(Names (I));
end loop;
end Namescatcha;
procedure SpaceBox(Names: in Names_Type; N : in Integer) is
begin
Put_Line (N * (+"+-----------+ "));
for I in 1..N loop
Put("! " & Names(I) & (10 - Length(Names(I))) * ' ' & "!");
if I = N then
Put("");
else
Put("<>---");
end if;
end loop;
New_Line;
Put_Line (N * (+"+-----------+ "));
end SpaceBox;
-- "Global" variables, unknown to
-- Objectcatcha, Namescatcha, SpaceBox:
N : Integer;
Names : Names_Type;
begin
Objectcatcha(N);
Put_Line("Enter the name of the objects: ");
Namescatcha(Names, N);
SpaceBox(Names, N);
end Names_in_Boxes;
Thanks Zerte, Jere and Brian for your examples, it's much appreciated. Unfortunately I can't use third party packages so that rules out Zertes solution and as for Jere Im sorry but Im just a simple codemonkey with very shallow ADA knowledge and your example is just too complicated for me. Even if I got the exact code and it worked I still wouldnt learn it because it differs too much from what my school is teaching out. Like the procedures not having in/out parameters lets say for one. Maybe I misunderstand and its not that bad but at first glance it seems too complex for my level of ADA.
Brians I thought would work but what it does is, because it loops Spacebox N times, it creates N^2 amount of boxes, and on separate lines, when I only need N boxes on one line. Is there any way we could perhaps patch up the code to fix this because it seemed promising?
Thanks again for your time all of you!
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Strings.Unbounded.Text_IO; use Ada.Strings.Unbounded.Text_IO;
procedure exercise is
Y : Ada.Text_IO.Count;
N : Integer;
Names : Unbounded_string;
type Names_Type is array (Positive range <>) of Unbounded_String;
procedure Objectcatcha (N : out Integer) is
begin
Put("Enter amount of objects: ");
Get(N);
Skip_Line;
end Objectcatcha;
procedure Namescatcha (Names: out Names_Type; N : in Integer) is
begin
for I in Names'Range loop
Get_Line(Names(I));
end loop;
end Namescatcha;
procedure SpaceBox (Names: in Names_Type; N : in Integer) is
begin
for I in 1..N loop
Put("+-----------+ ");
end loop;
New_Line;
for I in Names'Range loop
Put("! ");
Put(Names(I));
Y := Ada.Text_IO.Count(I);
Set_Col(13+18*(Y-1));
Put("!");
if I = N then
Put("");
else
Put("<>---");
end if;
end loop;
New_Line;
for I in 1..N loop
Put("+-----------+ ");
end loop;
end SpaceBox;
begin
Objectcatcha(N);
declare
A : Names_Type (1..N);
begin
Put("Enter the name of the objects: ");
Namescatcha(A, N);
SpaceBox(A, N);
end;
end exercise;
This code works exactly the way I wanted it to, so I think it's finally solved: yay! :)

Ada Integer'Width error

I have this Ada package code which theoretically is well written:
with Ada.Text_IO;
with Ada.Characters.Handling;
package body pkg_procedure is
procedure Read_Integer(Num : out Integer) is
Intro : constant Character := ASCII.LF;
Back : constant Character := ASCII.Del;
Char : Character;
Fin : Boolean := False;
Number : Natural := 0;
String_Number : String (1 .. Integer'Width – 1);
begin
Ada.Text_IO.New_line;
Ada.Text_IO.Put ("Write down a number and press Enter: ");
while not Fin loop
Ada.Text_IO.Get_Immediate (Char);
if Ada.Characters.Handling.Is_Digit (Char) then
Number := Number + 1;
String_Number(Number) := Char;
Ada.Text_IO.Put (Char);
elsif Char = Intro then
Fin := True;
elsif Number > 0 and Char = Back then
Ada.Text_IO.Put (ASCII.BS & ' ' & ASCII.BS);
Number := Number + 1;
end if;
end loop;
Number := Integer'Value (String_Number (1 .. Number));
Ada.Text_IO.New_line;
Num := Number;
exception
when Constraint_Error =>
Ada.Text_IO.New_line;
Ada.Text_IO.Put_Line ("Sorry: " & String_Number & " is too long to store it");
Num := 0;
end Read_Integer;
end pkg_procedure;
When I compile the program I obtain an error on this instruction which says: binary operator expected.
I can't fix it. I am totally new to this programming language.
The problem turns out to be that the - in
String_Number : String (1 .. Integer'Width – 1);
isn’t a plain - but a wide character with encoding e28093 - EN DASH.
I found this because, having seen that various exploratory changes didn’t show the error, I reverted to your original and tried compiling with -gnatw8 (input is UTF-8) as well as -gnatl for mixing messages with the program text, which resulted in
13. String_Number : String (1 .. Integer'Width – 1);
12
>>> binary operator expected
>>> illegal wide character
I suspect you provided us the wrong part of your code as this
with Ada.Text_Io; use Ada.Text_Io;
procedure TestInt is
number : String (1 .. Integer'Width - 1);
begin
Put_Line("Width=" & Integer'Image(Integer'Width - 1));
end TestInt;
works like a charm, if we ignore the warning on number which is not used, and return as expected :
Width= 10
Please be more precise and provide a full compilable sample.
I might also be interesting to tell us which compiler you use and on which operating system.

Storing string references

Problem
There are multiple ways to store string reference, so how would you do it in the example code? Currently the problem is with storing access to string because it is causing non-local pointer cannot point to local object. Is storing 'First and 'Last to reference a string a preferable way?
String reference storage
This record stores reference to a string. The First and Last is supposed to point to a string. The Name should be able to the same I think, but that will cause non-local pointer cannot point to local object when a local string is assigned to that. So the current work around solution is to use First and Last.
type Segment is record
First : Positive;
Last : Positive;
Length : Natural := 0;
Name : access String;
end record;
Assigning sub string reference
The commented line is causing non-local pointer cannot point to local object. This is because Item is local. Source is not local and that is the string I want sub string references from.
procedure Find (Source : aliased String; Separator : Character; Last : out Natural; Item_Array : out Segment_Array) is
P : Positive := Source'First;
begin
for I in Item_Array'Range loop
declare
Item : aliased String := Separated_String_Next (Source, Separator, P);
begin
exit when Item'Length = 0;
Item_Array (I).Length := Item'Length;
Item_Array (I).First := Item'First;
Item_Array (I).Last := Item'Last;
--Item_Array (I).Name := Item'Access;
Last := I;
end;
end loop;
end;
Example
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Main is
use Ada.Text_IO;
use Ada.Integer_Text_IO;
function Separated_String_Next (Source : String; Separator : Character; P : in out Positive) return String is
A : Positive := P;
B : Positive;
begin
while A <= Source'Last and then Source(A) = Separator loop
A := A + 1;
end loop;
P := A;
while P <= Source'Last and then Source(P) /= Separator loop
P := P + 1;
end loop;
B := P - 1;
while P <= Source'Last and then Source(P) = Separator loop
P := P + 1;
end loop;
return Source (A .. B);
end;
type Segment is record
First : Positive;
Last : Positive;
Length : Natural := 0;
Name : access String;
end record;
type Segment_Array is array (Integer range <>) of Segment;
procedure Find (Source : String; Separator : Character; Last : out Natural; Item_Array : out Segment_Array) is
P : Positive := Source'First;
begin
for I in Item_Array'Range loop
declare
Item : aliased String := Separated_String_Next (Source, Separator, P);
begin
exit when Item'Length = 0;
Item_Array (I).Length := Item'Length;
Item_Array (I).First := Item'First;
Item_Array (I).Last := Item'Last;
--Item_Array (I).Name := Item'Access;
Last := I;
end;
end loop;
end;
Source : String := ",,Item1,,,Item2,,Item3,,,,,,";
Item_Array : Segment_Array (1 .. 100);
Last : Natural;
begin
Find (Source, ',', Last, Item_Array);
Put_Line (Source);
Put_Line ("Index First Last Name");
for I in Item_Array (Item_Array'First .. Last)'Range loop
Put (I, 5);
Put (Item_Array (I).First, 6);
Put (Item_Array (I).Last, 5);
Put (" ");
Put (Source (Item_Array (I).First .. Item_Array (I).Last));
New_Line;
end loop;
end;
Output
,,Item1,,,Item2,,Item3,,,,,,
Index First Last Name
1 3 7 Item1
2 11 15 Item2
3 18 22 Item3
The error message tells you exactly what is wrong : Item is a string declared locally, i.e. on the stack, and you are assigning its address to an access type (pointer). I hope I don't need to explain why that won't work.
The immediate answer - which isn't wrong but isn't best practice either, is to allocate space for a new string - in a storage pool or on the heap - which is done with new.
Item : access String := new String'(Separated_String_Next (Source, Separator, P));
...
Item_Array (I).Name := Item;
Note that some other record members, at least, Length all appear to be completely redundant since it is merely a copy of its eponymous attributes, so should probably be eliminated (unless there's a part of the picture I can't see).
There are better answers. Sometimes you need to use access types, and handle their object lifetimes and all the ways they can go wrong. But more often their appearance is a hint that something in the design can be improved : for example:
the Unbounded_String may manage your strings more simply
You could use the length as a discriminant on the Segment record, and store the actual string (not an Access) in the record itself
Ada.Containers are a standard library of containers to abstract over handling the storage yourself (much as the STL is used in C++).
If you DO decide you need access types, it's better to use a named access type type Str_Access is access String; - then you can create a storage pool specific to Str_Acc types, and release the entire pool in one operation, to simplify object lifetime management and eliminate memory leaks.
Note the above essentially "deep copies" the slices of the Source string. If there is a specific need to "shallow copy" it - i.e. refer to the specific substrings in place - AND you can guarantee its object lifetime, this answer is not what you want. If so, please clarify the intent of the question.
For a "shallow copy" the approach in the question essentially fails because Item is already a deep copy ... on the stack.
The closest approach I can see is to make the source string aliassed ... you MUST do as you want each Segment to refer to it ... and pass its access to the Find procedure.
Then each Segment becomes a tuple of First, Last, (redundant Length) and access to the entire string (rather than a substring).
procedure Find (Source : access String; Separator : Character;
Last : out Natural; Item_Array : out Segment_Array) is
P : Positive := Source'First;
begin
for I in Item_Array'Range loop
declare
Item : String := Separated_String_Next (Source.all, Separator, P);
begin
exit when Item'Length = 0;
...
Item_Array (I).Name := Source;
Last := I;
end;
end loop;
end;
Source : aliased String := ",,Item1,,,Item2,,Item3,,,,,,";
...
Find (Source'access, ',', Last, Item_Array);
for I in Item_Array (Item_Array'First .. Last)'Range loop
...
Put (Item_Array (I).Name(Item_Array (I).First .. Item_Array (I).Last));
New_Line;
end loop;
A helper to extract a string from a Segment would probably be useful:
function get(S : Segment) return String is
begin
return S.Name(S.First .. S.Last);
end get;
...
Put (get(Item_Array (I));
The only rationale I can see for such a design is where the set of strings to be parsed or dissected will barely fit in memory so duplication must be avoided. Perhaps also embedded programming or some such discipline where dynamic (heap) allocation is discouraged or even illegal.
I see no solution involving address arithmetic within a string, since an array is not merely its contents - if you point within it, you lose the attributes. You can make the same criticism of the equivalent C design : you can identify the start of a substring with a pointer, but you can't just stick a null terminator at the end of the substring without breaking the original string.
Given the bigger picture ... what you need, rather than the low level details of how you want to achieve it, there are probably better solutions.

Ada- attempting to remove blank spaces from a string?

This question is in regards to a program I'm working on for an intro to programming class in Ada. The only packages I can use are the basic text, integer, and float packages. I'm writing a procedure right now to remove the spaces from the string and I keep getting a constraint error on the same line. This is the line:
if inputString(count) /= Character'Val(32) then
I have count set to start at 1 and then increment through a loop until the end of the string, which would in turn check each character of the string to see if it was different than the character value of a blank space (32). I was then going to include this line:
noSpace(count..count) := inputString(count..count)
where noSpace is to be the string with no spaces. My line of thinking may be completely off and I may need to approach this from a completely different angle.
Update: I've now managed to get past the error message and successfully remove the spaces from my string. But now when I print the new string there is a random variety of characters and shaded spaces after it. I believe the problem is caused because the new string is shorter than the previous one and the last characters are different.
Probably the bug is hid somewhere else.
Try this:
with Ada.Text_IO;
procedure String_Remove_Space is
function Count_Space( S : String ) return Natural is
Sum_Of_Space : Natural := 0;
begin
for I in S'range loop
if S(I) = ' ' then
Sum_Of_Space := Sum_Of_Space + 1;
end if;
end loop;
return Sum_Of_Space;
end Count_Space;
Input_String : String := "Apple is on the tree";
Input_No : String := "AppleIsOnTheTree";
No_Space : String(1..Input_String'Length - Count_Space(Input_String));
Index : Positive := 1;
begin
for I in Input_String'range loop
if Input_String(I) /= ' ' then
No_Space(Index) := Input_String(I);
Index := Index + 1;
end if;
end loop;
Ada.Text_IO.Put_Line(Input_String);
Ada.Text_IO.Put_Line(No_Space);
end String_Remove_Space;
If there are spaces in your input string, the valid part of the output string must be shorter. I imagine you wrote something like
noSpace : String := inputString;
to start things off with noSpace as long as it would need to be if there were no spaces in inputString? then, as you squeeze the spaces out, you seem to be writing garbage (undefined characters) to the end of noSpace. This doesn’t matter so long as you only deal with the valid part.
I tried this:
with Ada.Text_IO; use Ada.Text_IO;
procedure Lareaper is
function Strip_Space (S : String) return String is
Result : String := S;
Current : Positive := Result'First;
Last : Natural := Result'Last;
begin
loop
exit when Current > Last; -- processed the whole string
if Result (Current) = ' ' then
-- slide the rest of the string back one
Result (Current .. Last - 1) := Result (Current + 1 .. Last);
-- which reduces the length by 1 too
Last := Last - 1;
else
-- non-space character, skip
Current := Current + 1;
end if;
end loop;
-- return only the part of the result that doesn't contain spaces
return Result (Result'First .. Last);
end Strip_Space;
begin
Put_Line ('|' & Strip_Space ("") & '|');
Put_Line ('|' & Strip_Space (" ") & '|');
Put_Line ('|' & Strip_Space ("a") & '|');
Put_Line ('|' & Strip_Space ("ab") & '|');
Put_Line ('|' & Strip_Space (" a") & '|');
Put_Line ('|' & Strip_Space ("a ") & '|');
Put_Line ('|' & Strip_Space ("a b") & '|');
Put_Line ('|' & Strip_Space (" a b ") & '|');
end Lareaper;
which outputs
$ ./lareaper
||
||
|a|
|ab|
|a|
|a|
|ab|
|ab|
Count should start at InputString'First, not at 1.
In particular, when InputString is created as a slice/substring of another string, it is very likely that its first character is not at index 1.
Just to show you another possible solution, although one I would never use in real life:
function Strip_Space (S : String) return String is
begin
if S'Length = 0 then
return "";
elsif S (S'First) = ' ' then
return Strip_Space (S (S'First + 1 .. S'Last));
else
return S (S'First) & Strip_Space (S (S'First + 1 .. S'Last));
end if;
end Strip_Space;
And then there's the way I would really do it. I didn't read Simon's answer carefully enough to realize that he was sliding substrings around. The most efficient answer, I think, is similar to Mark's, but you don't actually need to count the spaces in the string first.
function Strip_Space (Input_String : String) return String is
No_Space : String(1..Input_String'Length);
Index : Positive := 1;
begin
for I in Input_String'range loop
if Input_String(I) /= ' ' then
No_Space(Index) := Input_String(I);
Index := Index + 1;
end if;
end loop;
return No_Space(1 .. Index - 1);
end Strip_Space;

Ada: Getting user input to a String(1..10) and filling the rest with whitespace

I have defined
subtype String10 is String(1..10);
and I am attempting to get keyboard input to it without having to manually enter whitespace before hitting enter. I tried get_line() but from some reason it wouldn't actually wait for input before outputting the get put() command, and I also think it will just leave whatever was in the string before there and not fill it with white space.
I know about and have used Bounded_String and Unbounded_String, but I am wondering if there is a way to make this work.
I've tried making a function for it:
--getString10--
procedure getString10(s : string10) is
c : character;
k : integer;
begin
for i in integer range 1..10 loop
get(c);
if Ada.Text_IO.End_Of_Line = false then
s(i) := c;
else
k := i;
exit;
end if;
end loop;
for i in integer range k..10 loop
s(i) := ' ';
end loop;
end getString10;
but, here, I know the s(i) doesn't work, and I don't think the
"if Ada.Text_IO.End_Of_Line = false then"
does what I'm hoping it will do either. It's kinda just a placeholder while I look for the actual way to do it.
I been searching for a couple hours now, but Ada documentation isn't as available or clear as other languages. I've found a lot about getting strings, but not what I'm looking for.
Just pre-initialize the string with spaces before calling Get_Line.
Here's a little program I just threw together:
with Ada.Text_IO; use Ada.Text_IO;
procedure Foo is
S: String(1 .. 10) := (others => ' ');
Last: Integer;
begin
Put("Enter S: ");
Get_Line(S, Last);
Put_Line("S = """ & S & """");
Put_Line("Last = " & Integer'Image(Last));
end Foo;
and the output I get when I run it:
Enter S: hello
S = "hello "
Last = 5
Another possibility, rather than pre-initializing the string, is to set the remainder to spaces after the Get_Line call:
with Ada.Text_IO; use Ada.Text_IO;
procedure Foo is
S: String(1 .. 10);
Last: Integer;
begin
Put("Enter S: ");
Get_Line(S, Last);
S(Last+1 .. S'Last) := (others => ' ');
Put_Line("S = """ & S & """");
Put_Line("Last = " & Integer'Image(Last));
end Foo;
For very large arrays, the latter approach might be more efficient because it doesn't assign the initial portion of the string twice, but in practice the difference is unlikely to be significant.
As an alternative, use either function Get_Line, which returns a fixed-length String that "has a lower bound of 1 and an upper bound of the number of characters read." The example Line_By_Line uses the variation that reads from a file. If need be, you can then use procedure Move to copy the Source string to the Target string; the procedure automatically pads with space by default.
Addendum: For example, this Line_Test pads with * and silently truncates long lines on the right.
with Ada.Integer_Text_IO;
with Ada.Strings.Fixed;
with Ada.Text_IO;
procedure Line_Test is
Line_Count : Natural := 0;
Buffer: String(1 .. 10);
begin
while not Ada.Text_IO.End_Of_File loop
declare
Line : String := Ada.Text_IO.Get_Line;
begin
Line_Count := Line_Count + 1;
Ada.Integer_Text_IO.Put(Line_Count, 0);
Ada.Text_IO.Put_Line(": " & Line);
Ada.Strings.Fixed.Move(
Source => Line,
Target => Buffer,
Drop => Ada.Strings.Right,
Justify => Ada.Strings.Left,
Pad => '*');
Ada.Integer_Text_IO.Put(Line_Count, 0);
Ada.Text_IO.Put_Line(": " & Buffer);
end;
end loop;
end Line_Test;

Resources