If I have defined an array type like
type Integer_Array is array(Natural range <>) of Integer;
and also use package Ada.Containers.Vectors as
package Integer_Vectors is new Ada.Containers.Vectors(
Element_Type => Integer,
Index_Type => Natural);
use Integer_Vectors;
How can I implement the following function?
function To_Integer_Array(V : Integer_Vectors.Vector) return Integer_Array;
What I have so far
Conceptually, it seems really easy:
Declare Temp_Arr as Integer_Array with capacity of V.Length
Iterate over V and copy all elements to Temp_Arr
Return Temp_Arr
Step 1. is giving me headaches though. I have tried:
function To_Integer_Array(V: Integer_Vectors.Vector) return Integer_Array is
Temp_Arr: Integer_Array(1..V.Length);
begin
-- Todo: copy values here
return Temp_Arr;
end To_Integer_Array;
This will throw
expected type "Standard.Integer"
found type "Ada.Containers.Count_Type"
While the error absolutely makes sense, I am unsure as to how I might solve it.
Is there a way to cast Ada.Containers.Count_Type to Standard.Integer?
Would there be another way to create an Integer_Array from Integer_Vector?
Thanks to Brian the declaration now works. The correct implementation for my function looks like this:
function To_Integer_Array(V: Integer_Vector) return Integer_Array is
Temp_Arr: Integer_Array(1..Natural(V.Length));
begin
for I in Temp_Arr'Range loop
Temp_Arr(I) := V.Element(I);
end loop;
return Temp_Arr;
end To_Integer_Array;
Related
I'm a bit of a beginner when it comes to Ada, and I'm trying to declare and use an array of strings of different lengths.
Using Ada'83 I can declare an array of variable length string constants as follows (example taken from the Ada FAQ)
type table is access String;
TESTS : constant array (Positive range 1..3) of table
:= ( 1 => new String'("One"),
2 => new String'("Two"),
3 => new String'("Three")
);
However much to my frustration even though the result appears to be an array of character arrays they don't behave as strings. When I try to compile the following code I get an error message 'Inconsistency detected during overload resolution [LRM 8.7]'
for COUNT in TESTS'Range loop
Put(TESTS(COUNT));
New_Line;
end loop;
However, I can print out the content of each of the 'strings' using the following code.
for COUNT in TEST'Range loop
for COUNTER in TEST(COUNT)'Range loop
Put(TEST(COUNT)(COUNTER));
end loop;
New_Line;
end loop;
Unfortunately I want to use the values to test some code that takes a string as a parameter, so this doesn't really help...
Is there a way to be to iterate over an array of string constants of varying length in Ada'83, or to convert the character arrays into strings of varying length.
Thanks
No, this isn't homework, and yes, I know I'm using an ancient compiler!
Test is undefined; I'll presume you mean Tests.
Table is not a string type; it is an access type. To reference the value that an access value designates, one uses .all:
Tests (Tests'First).all
is a string. Ada contains some shortcuts for access-to-array types to make them easier to use, allowing .all to be left off before attributes and indexing, which is why Tests (Count)'Range and Tests (Count) (Counter) work. To reference the whole value, though, .all is required:
Text_IO.Put_Line (Item => Tests (Counter).all);
However, a better approach would be to define a variable-length string abstraction and use that instead of an access type.
Thank you that works a treat - however, how would I go about defining
'a variable-length string abstraction' to do the same job?
Use private-types + access-types, perhaps like the following:
Package String_Abstraction is
Type DString is private;
Function "+"( Right : DString ) return String;
Function "+"( Right : String ) return DString;
Function "&"( Left, Right : String ) return DString;
--...
Private
Type Data(<>);
Type DString is access Data;
End String_Abstraction;
with implementation of:
Package Body String_Abstraction is
Type Data( Length : Natural ) is record
Text : String(1..Length) := (others => ASCII.NUL);
end record;
Function "+"( Right : String ) return DString is
Begin
Return New Data'( Text => Right, Length => Right'Length );
End "+";
Function "&"( Left, Right : String ) return DString is
Begin
Return +(Left & Right);
End "&";
Function "+"( Right : DString ) return String is
Begin
Return Right.Text;
End "+";
--...
End String_Abstraction;
Which could be used as follows:
Table : Constant Array(Positive range <>) of String_Abstraction.DString:=
( String_Abstraction."+"( "This" ),
String_Abstraction."+"( "EXAMPLE" ),
String_Abstraction."+"( "list" ),
String_Abstraction."+"( "exists." )
);
and
Print_Table:
For Index in Table'Range Loop
Declare
Use String_Abstraction;
Item : DString renames Table(Index);
Begin
Ada.Text_IO.Put_Line( +Item );
End;
End loop Print_Table;
If you had a use prior to the declaration of table, you could have:
Use String_Abstraction;
Table : Constant Array(Positive range <>) of String_Abstraction.DString:=
( +"This",
+"EXAMPLE",
+"list",
+"exists."
);
It's certainly not complete, but that gives you the basic idea of how to do it.
GNAT Studio Community Edition is free to use for freelance use and hobby going coders. It comes pre-installed with a decent Ada 2012 compiler. I had to also install a small add on called Ada GDE to get the code to run.
If your looking for an up to date book to study from, I highly recommend Ada 2012 by john Barnes. It's a bit of a beast at over 900 pages long, but he goes into good detail on concepts and provides plenty of sample code to help. He also wrote one for Ada 95 I believe.
I am trying to pass an array of Unbounded_String to a function, and I don't care about the range of the index, as the function is going to loop over each element.
The (element1, element2) syntax automatically starts at the first index value in the range, then increments for the second value given, which works fine for more than one value. However, for a single value, this cannot be used as the parentheses are considered superfluous.
This code shows the error messages for each of the attempts I have made. (1) works, but (2), the preferable syntax for passing a single-element array, does not. (3) works, and is given as an answer to this similar question. However, this hardcodes the first index of the range into the calling side; if the String_Array implementation changes, all the call-sites have to be changed, even though they don't care about the index values used.
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure Main is
function "+"(S: String) return Ada.Strings.Unbounded.Unbounded_String
renames Ada.Strings.Unbounded.To_Unbounded_String;
type String_Array is array (Positive range <>) of Unbounded_String;
procedure Foo(input : in String_Array) is
begin
null;
end Foo;
begin
Foo((+"one", +"two")); --(1)
--Foo((+"only")); --(2) positional aggregate cannot have one component
Foo((1 => +"only")); --(3)
--Foo((String_Array'First => +"only")); --(4) prefix for "First" attribute must be constrained array
--Foo((String_Array'Range => +"only")); --(5) prefix for "Range" attribute must be constrained array
--Foo((String_Array'Range'First => +"only")); --(6) range attribute cannot be used in expression
--Foo((String_Array'Range'Type_Class'First => +"only")); --(7) range attribute cannot be used in expression
end Main;
What you want (2) is indeed impossible as it could be mistaken for a parenthesized expression (see http://www.adaic.org/resources/add_content/standards/12aarm/html/AA-4-3-3.html note 10).
If you really want to avoid expression (3) for the reasons you stated, as workaround, you could define a function to handle the one-element array case:
function Singleton_String_Array (S: String) return String_Array is ((1 => + S));
-- one element call
Foo(Singleton_String_Array ("only"));
It reuse your expression (3) but the first index hardcoding is no longer done on call site.
You can also overload your foo function to handle the special one-element case:
procedure Process_String (input : in Ada.Strings.Unbounded.Unbounded_String) is
begin
null;
end Process_String;
procedure Foo(input : in String_Array) is
begin
for string of input loop
Process_String (string);
end loop;
end Foo;
procedure Foo(input : in Ada.Strings.Unbounded.Unbounded_String) is
begin
Process_String (input);
end Foo;
-- One element call
Foo(+"only");
The short answer is that all array objects must be constrained, which means callers usually have to decide on the array bounds.
However, you know the index type, and could do
Foo((Positive'First => +"only"));
which doesn't really answer your question, since someone may still fiddle with the array range, and there's not really any guard against that.
Adding a new subtype as the range may be a viable solution, though:
subtype String_Array_Range is Positive;
type String_Array is array (String_Array_Range range <>) of Unbounded_String;
...
Foo((String_Array_Range'First => +"only"));
Any fiddling can now be done on the String_Array_Range subtype without affecting any callers. But there's still no guarantee against evil programmers changing the index type of the array itself...
type String_Array is array (Positive range <>) of Unbounded_String;
Declares a type of array but doesn't provide the size.
Remember that an array has a static size.
So String_Array'First and String_Array'Range don't match to anything.
If you declared
type my_String_Array is String_Array(1 .. 35);
my_arr : my_String_Array
Then my_arr'First denotes 1 and my_arr'Range denotes 1..35.
As long as you don't put a contraint on the type, you won't have access to these attributes.
I have a large array in C that I wish to move into my ada project. That array is used to store filenames for assets which will later be loaded. It looks something like:
const char *filenames[NUMBER_OF_FILES] = {
/* file[0] */ "res/0.file",
/* ... */
/* file[n] */ "res/some_more/complicated/file.name"
};
I want to move this into an ada package body, but can't find a decent way to do it. Obviously my first attempt was:
filenames : constant array (File_Index) of String := (
index_0 => "res/0.file",
-- ...
index_n => "res/some_more/complicated/file.name"
);
But of course String is an unconstrained type, so Ada won't allow that. I switched it to use Unbounded_Strings, which worked, but was very ugly (having to wrap each string with To_Unbounded_String.
Is there any way to make an array of unconstrained types whose size will be known at compile time like this, or do I have to use unbounded strings?
It’s a bit low-level and repetitive, but perhaps you can create a little program (maybe even in Ada!) to generate something like
with Ada.Text_IO; use Ada.Text_IO;
procedure Lambdabeta is
F1 : aliased constant String := "res/0.file";
F2 : aliased constant String := "res/some_more/complicated/file.name";
type Strings is array (Positive range <>) of access constant String;
Filenames : constant Strings := (F1'Access,
F2'Access);
begin
for J in Filenames'Range loop
Put_Line (Filenames (J).all);
end loop;
end Lambdabeta;
See also this answer on minimising the pain of using To_Unbounded_String.
Arrays can't contain objects of indefinite types.
Basically you have two options:
Use another container than an array.
Encapsulate the strings in a definite type.
So you could use Ada.Containers.Indefinite_Vectors instead of an array:
with Ada.Containers.Indefinite_Vectors;
package Settings is
------------------------------------------------------------------
-- You may want to put this block in a separate package:
package String_Vectors is
new Ada.Containers.Indefinite_Vectors (Index_Type => Positive,
Element_Type => String);
function "+" (Left, Right : String) return String_Vectors.Vector
renames String_Vectors."&";
function "+" (Left : String_Vectors.Vector;
Right : String) return String_Vectors.Vector
renames String_Vectors."&";
------------------------------------------------------------------
File_Names : constant String_Vectors.Vector :=
"/some/file/name" +
"/var/spool/mail/mine" +
"/etc/passwd";
end Settings;
So you could use Ada.Strings.Unbounded.Unbounded_String instead of String:
with Ada.Strings.Unbounded;
package Settings_V2 is
function "+" (Item : in String) return Ada.Strings.Unbounded.Unbounded_String
renames Ada.Strings.Unbounded.To_Unbounded_String;
type String_Array is array (Positive range <>)
of Ada.Strings.Unbounded.Unbounded_String;
File_Names : constant String_Array :=
(+"/some/file/name",
+"/var/spool/mail/mine",
+"/etc/passwd");
end Settings_V2;
What has not yet been mentioned is that you can just use a function:
subtype File_Index is Integer range 1 .. 3;
function Filename (Index : File_Index) return String is
begin
case Index is
when 1 => return "res/0.file";
when 2 => return "res/1.file";
when 3 => return "res/some_more/complicated/file.name";
end case;
end Filename;
Using Filename (1) in your code is identical to accessing an array element.
Is it possible to pass to a function or procedure a dynamic array as optional parameter ? If yes, how ?
I have tried in this way :
procedure testp (str : string; var arr : StringArray = nil);
begin
str := 'Ciao Alessio !';
SetLength(arr, 2);
arr[0] := 'Ale';
arr[1] := 'Ale';
end;
but it gives : default parameter 'arr' must be by-value or const .
I'm using Delphi 7, but if not possible with Delphi 7, is it possible with newer version of Delphi or Free Pascal ?
Default parameters can only be specified for const or by value parameters. They cannot be specified for var parameters.
To achieve the caller flexibility you are looking for you'll need to use overloads.
procedure foo(var arr: StringArray); overload;
begin
.... do stuff
end;
procedure foo; overload;
var
arr: StringArray;
begin
foo(arr);
end;
The error message means exactly what it says, and it has nothing to do with the parameter being a dynamic array. The compiler would have rejected that code no matter what type the parameter had because you're not allowed to give default values for parameters passed by reference.
To make an optional reference parameter, use overloading to give two versions of the function. Change your current function to receive its parameter by value or const, as the compiler advises, and then declare another function without that parameter, as follows:
procedure testp (str : string);
var
arr: StringArray;
begin
testp(str, arr);
end;
That is, declare a dummy parameter and pass it to the "real" function. Then simply throw away the value it returns.
If calculation of the reference value is expensive, then the implementation of the one-parameter version of testp will instead duplicate more of the code from the two-argument version.
I have a problem in Ada concurrent programming. I have to write a simple program in Ada that creates N of tasks of the same type, where N is the input from keyboard. The problem is that I have to know N before compilation...
I have tried to declare a separate type:
TYPE my_arr IS ARRAY(INTEGER RANGE <>) OF some_task;
and later in BEGIN section of main procedure:
DECLARE arr: my_arr(1 .. N);
but I get the error
unconstrained element type in array declaration
Which (I think) means that task type some_task is of unknown size.
Can anyone please help ?
This is a rewrite of the original answer, now that we know that the task type in
question is discriminated.
You are passing a value to each task via a 'discriminant' without a default, which makes the task type unconstrained; you can't declare an object of the type without supplying a value for the discriminant (and supplying a default wouldn't help, because once the object has been created the discriminant can't be changed).
One common approach to this uses access types:
with Ada.Integer_Text_IO;
with Ada.Text_IO;
procedure Maciek is
task type T (Param : Integer);
type T_P is access T;
type My_Arr is array (Integer range <>) of T_P;
task body T is
begin
Ada.Text_IO.Put_Line ("t" & Param'Img);
end T;
N, M : Integer;
begin
Ada.Text_IO.Put ("number of tasks: ");
Ada.Integer_Text_IO.Get (N);
Ada.Text_IO.Put ("parameter: ");
Ada.Integer_Text_IO.Get (M);
declare
-- Create an array of the required size and populate it with
-- newly allocated T's, each constrained by the input
-- parameter.
Arr : My_Arr (1 .. N) := (others => new T (Param => M));
begin
null;
end;
end Maciek;
You may need to deallocate the newed tasks once they are completed; in the code above, the tasks' memory is leaked on exit from the declare block.
Allocate them in a 'declare block', or dynamically allocate them:
type My_Arr is array (Integer range <>) of Some_Task;
type My_Arr_Ptr is access My_Arr;
Arr : My_Arr_Ptr;
begin
-- Get the value of N
-- Dynamic allocation
Arr := new My_Arr(1 .. N);
declare
Arr_On_Stack : My_Arr(1 .. N);
begin
-- Do stuff
end;
end;
If you are sure at runtime, how many tasks you want, you can pass that as command-line argument.
with Ada.Text_IO; use ADA.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Command_Line;
with Ada.Strings;
procedure multiple_task is
No_of_tasks : Integer := Integer'Value(Ada.Command_Line.Argument(1));
task type Simple_tasks;
task body Simple_tasks is
begin
Put_Line("I'm a simple task");
end Simple_tasks;
type task_array is array (Integer range <>) of Simple_tasks;
Array_1 : task_array(1 .. No_of_tasks);
begin
null;
end multiple_task;
And run as
> multiple_task.exe 3
I'm a simple task
I'm a simple task
I'm a simple task