In the code I inherited, a buffer is passed to a procedure using its start address and length as parameters. This procedure code uses inline assembly language to process the buffer contents.
In a new procedure that uses the same parameters, I want to refer to the buffer as an array but I want to use the same parameters and their types that the existing procedure uses. This is so I don't have to make intrusive modifications to the original code except to use the same calling signature, which is like this:
procedure Push_Buffer(
Source_Address : Address;
Count : Natural);
In this case, the buffer is just an array of machine words. I want to refer to it as an array of machine words, and already have types (Word and Buffer_Type) that are used elsewhere without a problem. (The objects of unconstrained type Buffer_Type have their constraints defined where they are used.)
I would like to refer to the buffer that's being passed by address as an array within my procedure. How would I do this?
Like this (which works with -gnat83, but might not work with a real Ada83 compiler; I don’t have one to check with):
with Text_IO; use Text_IO;
with System;
procedure Push is
type Integers is array (Positive range <>) of Integer;
procedure Push_Buffer (Source_Address : System.Address;
Count : Natural) is
Source : Integers (1 .. Count);
for Source use at Source_Address;
begin
for J in Source'Range loop
Put_Line (Integer'Image (J) & " => " & Integer'Image (Source (J)));
end loop;
end Push_Buffer;
begin
declare
Data : Integers (1 .. 3) := (4, 5, 6);
begin
Push_Buffer (Data'Address, Data'Length);
end;
end Push;
As a side note, probably not relevant to your situation: if there was any question of default initialization of the object whose address you’ve specified (for example, if it’s a structure containing access variables, which are default-initialized to null) you would need to suppress the initialization by writing the declaration as
Source : Integers (1 .. Count);
pragma Import (Ada, Source);
for Source use at Source_Address;
(or something compiler-dependent like that; GNAT says warning: (Ada 83) pragma "Import" is non-standard). See the GNAT Reference Manual 8.15, Address Clauses, about half-way down.
I would use an unconstrained array as the parameter, rather than a System.Address. This ensures that the compiler does not pass the bound information (aka "fat pointers" in gcc and gnat), and should be compatible with a pragma import.
type Unconstrained_Integer_Array is array (Positive) of Integer;
procedure Push_Buffer (Buffer : Unconstrained_Integer_Array;
Length : Natural)
is
begin
for A in 1 .. Length loop
...
end loop;
end Push_Buffer;
We use similar tricks when interfacing with C in the GtkAda bindings
Related
This question already has answers here:
How to use a variable in an array?
(2 answers)
Closed 3 months ago.
I have a code written in Pascal. I have a record type called recordPembeli and I have an array[1..x] of recordPembeli. The x in this array will be the length of the data which the user determines in the body of program readln(x).
Here is the program:
program iniSesiDua;
uses crt;
type
recordPembeli = record
nama : string[5];
jumlahPembelian : integer;
end;
var
x : integer
dataPembelian : array[1..x] of recordPembeli;
begin
clrscr;
write('Masukkan jumlah pembeli : ');readln(x);
readln;
end.
I try it and it says :
Warning: Variable "x" does not seem to be initialized
Error: Can't evaluate constant expression
Can I even determine the length of the data by user input in array of record or is it forbidden?
Can I even determine the length of the data by user input in array of records...?
Yes you can, but the array must be dynamic, which means that the number of elements is not defined at compile time, but at run time:
var
x: integer;
dataPembelian: array of recordPembeli; // dynamic array, no size
begin
write('Masukkan jumlah pembeli : ');readln(x); // Get length from user
SetLength(dataPembelian, x); // create the records in the array
Note that the indexing of the array elements starts from 0.
The bounds of an array (unless it is a dynamic array) must be a compile time constant.
You are using a variable, and one that hasn't been initialized at that.
The variable determining the length x and the array’s declaration cannot be in the same block.
You will need to use a nested block like this:
program iniSesiDua(input, output);
type
recordPembeli = record
nama: string(5);
jumlahPembelian: integer;
end;
var
x: integer;
procedure readAndProcessPembelin;
var
dataPembelian: array[1..x] of recordPembeli;
begin
{ You read and process data here. }
end;
{ === MAIN =============================================== }
begin
write('Masukkan jumlah pembeli : ');
readLn(x);
readAndProcessPembelin
end.
Non-constant subrange bounds are an extension of ISO standard 10206 “Extended Pascal”.
Your error messages seem like to have been emitted by the FPC (Free Pascal Compiler).
This processor does not support Extended Pascal (yet).
There you are forced to use an alternative data type,
e. g. a linked list or more conveniently “dynamic” arrays.
So, I'm coding in Ada for the first time and have to create this pre-populated array of strings, so I can use a random method and call a different value each time.
poolOfWords: constant array(1..50) of string := ("gum","sin","for","cry","lug","bye","fly",
"ugly", "each","from","work","talk","with","self", "pizza","thing","feign","friend",
"elbow","fault", "dirty","budget","spirit","quaint","maiden","escort","pickax",
"example","tension","quinine","kidney","replica","sleeper", "triangle","kangaroo",
"mahogany","sergeant","sequence","moustache","dangerous","scientist",
"different","quiescent", "magistrate","erroneously","loudspeaker",
"phytotoxic","matrimonial","parasympathomimetic","thigmotropism");
But when I try to compile I get this error: "unconstrained element type in array declaration".
I Googled all over internet and couldn't find a solution to that. I even tried to use unbounded_string, but didn't work as well. It works fine when I pre-populate an array of integers, floats, but when it come to strings it's not working.
Can someone help me and explain what's going on and how to solve it? I really don't want to declare the array and then populate each of the 50 indexes one by one.
Your problem stems from the fact that String in Ada is an unconstrained type. This means, roughly, that you need to specify "something" in order to have a usable type. In the case of String you need to specify the string length, since a String is just an array of Character. If you say something like
X : String;
the compiler complains since it does not know the length of X and it does not know how much memory to reserve for X. You need to say something like
X : String(1..6);
or something like
X : String := "foobar";
Note that in the second case you do not need to specify the index range since the compiler is able to deduce it from the fact that you use a 6-Character string to initialize X.
This explains why it is illegal to declare an array of just String. When you declare an array the compiler needs to know the size of every entry (all the entries must share the same size, this makes access to a random entry of the array very efficient). If you declare an array of just String, the compiler would not know how much memory to allocate to every entry since the length is unspecified.
Note that this instead would work (disclaimer: I have not an Ada compiler at hand now, so I cannot check the code)
subtype ZIP_Code is String (1..5);
type ZIP_Code_Array is array(Positive range <>) of ZIP_Code;
since now the compiler knows the size of every entry of a ZIP_Code_Array (5 Character).
In your case, however, the strings have all different sizes. I can see two possible solutions
If the maximum length is known in advance you can use the subtype approach shown above and pad the strings with spaces. It depends on your context if this is a suitable solution or not
The approach that I typically use is to create an array of Unconstrained_String that I initialize as follows
function "+"(X: String) return Unbounded_String
renames To_Unbounded_String;
poolOfWords: constant array(1..50) of Unbounded_String :=
(+"gum",
+"sin",
+"for",
-- and so on...
);
Note the renames that defines the unary + as an alias for To_Unbounded_String. This is just syntactic sugar that allows you to write +"gum" instead of To_Unbounded_String("gum"). Someone cringes at this type of syntactic sugar (and I am not so keen either), but in this case I think it is worth since it makes the code "lighter." It would be nice to have the definition of unary + near its use, just for clarity.
An alternative to the use of Unbounded_String is to use the generic package Bounded_Strings and define poolOfWords as an array of Bounded_String. A Bounded_String can hold strings with variable length, as long as the length is smaller than the bound specified when instantiating the Bounded_Strings package. In order to convert back and forth String you can use the same "rename trick."
My personal preference goes to the Unbounded_String, unless I have some special reason that forces me to use the Bounded_String.
Another option is to use Ada's standard containers. For example:
Package String_Holder is new Ada.Containers.Indefinite_Holders(
"=" => Ada.Strings.Equal_Case_Insensitive,
Element_Type => String
);
Function "+"( Right : String_Holder.Holder ) return String
renames String_Holder.Element;
Function "+"( Right : String ) return String_Holder.Holder
renames String_Holder.To_Holder;
Allows you to store in the holder varying sized values of type String, so you could forego any dependence on Ada.Strings.Unbounded_String and use something like:
poolOfWords: constant array(Positive range <>) of String_Holder.Holder :=
+"gum", +"sin", +"for", +"cry", -- ...
+"Stuff"
);
Function Get_Word(Index : Positive) return String is
(+poolOfWords(Index)) with Pre => Index in poolOfWords'Range;
Alternatively, you could use Ada.Containers.Indefinite_Vectors like so:
Package String_Vector is new Ada.Containers.Indefinite_Vectors(
"=" => Ada.Strings.Equal_Case_Insensitive,
Index_Type => Positive,
Element_Type => String
);
though there is one problem with this in Ada 2012: You can't initialize such a vector directly within the declaration, but can initialize it via function-call:
Function Populate return String_Vector.Vector is
Begin
Return Result : String_Vector.Vector do
Result.Append( "some" );
Result.Append( "data" );
Result.Append( "HERE" );
End Return;
End Populate;
Being able to say X : String_Vectors.Vector := ("this", "that", "the other") is in Ada 2020 .
I want to make a list of strings in a Testbench to load different files for example.
I tried:
type tastring ARRAY(iADCCount_C-1 downto 0) of string;
constant Filenames : tastring := ("file.txt",
"anotherfile.txt",
"jetanotherfile.txt");
Its not possible to have variable length strings in an array.
Also:
type tpstring is access string;
type tpastring is ARRAY(iADCCount_C-1 downto 0) of tpstring;
constant Filenames : tpastring := (new string'("file.txt"),
new string'(anotherfile.txt"),
new string'(jetanotherfile.txt"));
Does not work! You cannot make an access type constant. Do I miss something? Is there a way to make a list of Strings without padding them to the same size?
You are almost correct :)
The second code snippet must use a variable, because access types can only be used for objects of kind variable.
type line_vector is array(iADCCount_C-1 downto 0) of line;
variable Filenames : line_vector := (
new string'("file.txt"),
new string'("anotherfile.txt"),
new string'("jetanotherfile.txt")
);
Note 1: added missing " characters.
Note 2: type line is already defined in VHDL.
Note 3: type line_vector will be defined by VHDL-2017.
As an alternative, you can fill all string with character NUL. You might want to implement two functions for sizing the string to the constant's size and to trim the string (remove trailing NUL characters.
See this answer. AFAIK it is impossible to have arrays of variable-length strings.
If you implement custom trim functions as suggested in the answer, try using a fixed spacing character that cannot exist in file names(like ? for windows), as it will also ensure no problems with NUL-characters or other non-prinatbles that can cause hiccoughs with different synthesis tools.
I would like to make a procedure that take array of shortstring as argument
procedure f(const a, b: Array of shortstring);
I would like to call this with arrays of known length and shortstrings of known length e.g.
var
A, B: array[1..2] of string[5];
C, D: array[1..40] of string[12];
begin
f(A,B);
f(C,D);
end;
This result in an compiler error E2008 Incompatible types.
Why is that? Can I write a procedure that can take arrays of shortstring (any length of arrays/strings)?
Why use shortstring?
The shortstings are fields in an existing record. There are alot of these record with thousand of shortstrings. In an effort to migrate data from turbo power B-Tree Filer to SQL databases one step is to convert the record to a dataset, and the back to a record, to confirm all fields are converted correctly both directions. I have been using CompareMem on the records to check this, but it does not provide enough information as to which field a conversion error is in. Thus a small program was created, which from the record definition can generate code to compare the two records. It was for this code generator I needed a function to compare shortstrings. It ended up using CompareMem on the shortstrings.
A ShortString is 0 to 255 characters long. The length of a ShortString can change dynamically, but memory is a statically allocated 256 bytes, the first byte stores the length of the string, and the remaining 255 bytes are available for characters, whilist string[5] declared in this way allocate only as much memory as the type requires (5 byte + 1 byte for length).
you could use type
type
MyString = string[5];
...
procedure f(const a, b: Array of MyString);
...
var
A, B: array[1..2] of MyString;
begin
f(A,B);
end;
In a similar situation I've used the following:
type
TOpenArrayOfOpenString = record
strict private
FSizeOfString: Integer;
FpStart: PChar;
FArrayLength: Integer;
function GetItemPtr(AIndex: Integer): PShortString;
public
constructor Init(var AFirstString: Openstring; AArrayLength: Integer);
function Equals(const AArray: TOpenArrayOfOpenString): Boolean;
property SizeOfString: Integer read FSizeOfString;
property pStart: PChar read FpStart;
property ArrayLength: Integer read FArrayLength;
property ItemPtrs[AIndex: Integer]: PShortString read GetItemPtr; default;
end;
{ TOpenArrayOfOpenString }
constructor TOpenArrayOfOpenString.Init(var AFirstString: Openstring; AArrayLength: Integer);
begin
FSizeOfString := SizeOf(AFirstString);
FpStart := #AFirstString[0]; // incl. length byte!
FArrayLength := AArrayLength;
end;
function TOpenArrayOfOpenString.Equals(const AArray: TOpenArrayOfOpenString): Boolean;
begin
Result := CompareMem(pStart, AArray.pStart, SizeOfString * ArrayLength);
end;
function TOpenArrayOfOpenString.GetItemPtr(AIndex: Integer): PShortString;
begin
Result := PShortString(pStart + AIndex * SizeOfString);
end;
You could use it like this:
procedure f(const a: TOpenArrayOfOpenString);
var
i: Integer;
begin
for i := 0 to Pred(a.ArrayLength) do
Writeln(a[i]^);
end;
procedure Test;
var
A: array[1..2] of string[5];
C: array[1..40] of string[12];
begin
f(TOpenArrayOfOpenString.Init(A[1], Length(A)));
f(TOpenArrayOfOpenString.Init(C[1], Length(C)));
end;
It's not as elegant as a solution built into the language could be and it is a bit hacky as it relies on the fact/hope/... that the strings in the array are laid out contiguously. But it worked for me for some time now.
type
shortStrings =array[1..2] of string[5];
...
a,b : shortString;
..
procedure rock(a,b : shortStrings);
..
You are combining two different kinds of open array.
First, there is the classic Turbo Pascal "string" (also called "openstring" in Delphi IIRC) which is essentially string[255]. As string[255] is a superset of all shortstrings, the open array aspect simply converts all shortstring types to it.
The "array of xx" syntax is the Delphi (4+?) open array. It is an open array of any type, not just strings, and the syntax to call it is f(nonarrayparam,[arrayelement0,arrayelement1]);
Somehow you seem to mix both syntaxes, and even aggrevate it by adding CONST which sollicits pass by reference and excludes conversions.
I think you assume shortstring has an performance advantage. It has, in some cases. Open array is not one of those cases. Not even in TP :-)
E.g. I have this array:
type
OptionRange = array[ 1..9 ] of integer;
How do I check if array[x] exists?
Actually, I want to limit user input with the array index. Am I doing the wrong thing? Is there a better practical solution?
In Free Pascal and the Borland dialects (and perhaps elsewhere as well), you can use the Low and High functions on the array type or a variable of the array type. I see this used most often to determine the bounds for for loops:
var
range: OptionRange;
i: Integer;
begin
for i := Low(range) to High(range) do begin
range[i] := GetOptionRangeElement(i);
end;
end;
You can also define a subrange type and then use it to define both the array and the index variables you use on the array:
type
OptionRangeIndex = 1..9;
OptionRange = array[OptionRangeIndex] of Integer;
var
range: OptionRange;
i: OptionRangeIndex;
Then, when you have range checking enabled (assuming your compiler offers such a feature) and you use a value that's outside the range for OptionRange indices, you'll get a run-time error that you can catch and handle however you want.
I'm not really sure what an option range is or why an array of nine integers would be used to represent one, but I figure that's a name-selection issue.