How to declare a static array with variable length in delphi? - arrays

I need an array, that is optimized for one-time-initialization at runtime, with a given length. So memory should be allocated at runtime, but I don't need to change its length.
Is there a array-type other than the pure dynamic array? (it seems to be not the optimal choice for this task)
Bonus would be, if the initialized array is indexable via pointer-iteration, so all it's elements are allocated consecutive in memory.
Is this all just a daydream of a non-experienced programmer, or is there a possibility to achieve this?
I could imagine to do this with manual memory allocation, but maybe there's another way.
Edit:
My main concern is the reading and writing speed of the array.

Just use an external Count: integer variable, and use the dynamic array length as the "capacity" of the array. It would avoid most memory allocation, if the initial capacity of the array is well defined.
In practice, TList<T>, as defined in the System.Generics.Collections unit, is using this scheme: it stores internally an array, but it has its own Count property. I suspect this is what you were looking for.
For a more low-level stuff, with more features (like JSON or binary serialization, or fast lookup via a hash of one or several properties), you may take a look at our TDynArray dynamic array wrapper. Those are just wrappers on existing dynamic arrays, not data holder like TList<T>. And they work from Delphi 5 or older, and also FPC.

You can encapsulate what you desire in a generic type. Like this:
type
TFixedLengthArray<T> = record
strict private
FItems: TArray<T>;
FLength: Integer;
function GetItem(Index: Integer): T; inline;
procedure SetItem(Index: Integer; const Value: T); inline;
public
property Length: Integer read FLength;
property Items[Index: Integer]: T read GetItem write SetItem; default;
class function New(const Values: array of T): TFixedLengthArray<T>; static;
end;
{ TFixedLengthArray<T> }
class function TFixedLengthArray<T>.New(const Values: array of T): TFixedLengthArray<T>;
var
i: Integer;
begin
Result.FLength := System.Length(Values);
SetLength(Result.FItems, Result.FLength);
for i := 0 to Result.FLength-1 do begin
Result.FItems[i] := Values[i];
end;
end;
function TFixedLengthArray<T>.GetItem(Index: Integer): T;
begin
Result := FItems[Index];
end;
procedure TFixedLengthArray<T>.SetItem(Index: Integer; const Value: T);
begin
FItems[Index] := Value;
end;
Create a new one like this:
var
MyArray: TFixedLengthArray<Integer>;
....
MyArray: TFixedLengthArray<Integer>.New([1, 42, 666]);
Access items like this:
for i := 0 to MyArray.Length-1 do
Writeln(MyArray[i]);
This just wraps a dynamic array. Elements are contiguous. The length of the array is determined once and for all then a new instance is created.
One thing to watch out for here is that the type will behave like a reference type since its data is stored in a reference type. That is, the assignment operator on this type will behave in the same manner as dynamic array assignment.
So if we have two variables of this type, arr1 and arr2 then the following occurs:
arr1 := arr2;
arr1[0] := 42;
Assert(arr2[0] = 42);
If you wanted to make the type behave like a true value then you would implement copy-on-write inside SetItem.
Update
Your edit to the question changes is significantly. It seems that you are in fact concerned more with performance than encapsulation.
The inlining of the item accessor methods in the above type means that the performance characteristics should be close to that of an array. The access will still be O(1), but it is quite plausible that the inliner/optimiser is weak and fails to emit the most optimal code.
Before you decide that you must use arrays to obtain the absolute ultimate performance, do some real world benchmarking. It seems to me to be quite unlikely that the code to read/write from an array is really a bottleneck. Most likely the bottleneck will be what you then do with the values in the array.

Related

Is it possible to get length of (static/dynamic) arrays from their references?

I want to pass reference of an array to a function that needs the length of array. I want to know if I have to pass its length as well or I can retrieve it from the array reference.
uses
Vcl.Dialogs, System.SysUtils, System.Types;
type
IntegerArray = array[0..$effffff] of Integer; // defined in System
PIntegerArray = ^IntegerArray; // defined in System
procedure Foo(const P: PIntegerArray);
begin
ShowMessage(IntToStr(Length(P^)));
end;
const
A: array[0..2] of Integer = (1, 2, 3);
var
B: TIntegerDynArray;
initialization
Foo(#A);
B := [4, 5, 6];
Foo(#B[0]);
end.
To accomplish this, you need to declare an open array parameter:
procedure Foo(const A: array of integer);
begin
ShowMessage('Length of array:'+IntToStr(High(A)+1));
end;
Pass both dynamic and static arrays to the procedure, and the arrays length is given by System.High.
Open array : The value, of type Integer, giving the number of elements in the actual parameter, minus one.
Your question amounts to the following:
Given the address of the first element of an array, which could be either static or dynamic, can I find the length of that array?
The answer is no. There are two factors in your way.
You can't tell whether the array is dynamic or static.
Even if you knew the array was static, you would not be able to find its length without compile time knowledge of its type.

Delete element from a static array

I am trying to remove an item from an array.
The array is not dynamic!
I found many examples on how to do it for the dynamic variant but none for the static.
example from delphi:
var
A: array of integer;
begin
...
A:=[1,2,3,4];
Delete(A,1,2); //A will become [1,4]
...
end;
example from another site:
type
TIntArray = array of Integer;
procedure DeleteArrayElement(var AArray: TIntArray; const AIndex: Integer);
begin
Move(AArray[AIndex + 1], AArray[AIndex], SizeOf(AArray[0]) * (Length(AArray) - AIndex - 1));
SetLength(AArray, Length(AArray) - 1);
end;
...
//call via
DeleteArrayElement(IntArray, 3);
...
My array is defined as 0 .. 11 so this is not dynamic(i guess)?
When I try to use the SetLength function it says incompatible types.
Any idea how to solve this?
When you declare a static array you tell the compiler that the memory for the whole array should be allocated and retained until the program is terminated (if allocated in global space).
You cannot change the size of a static array. This is the purpose why dynamic arrays are there in Delphi.
The Embarcadero documentation for static arrays says:
If you create a static array but don't assign values to all its
elements, the unused elements are still allocated and contain random
data; they are like uninitialized variables.

Can Ada functions return arrays?

I read somewhere that Ada allows a function only to return a single item. Since an array can hold multiple items does this mean that I can return the array as a whole or must I return only a single index of the array?
Yes, an Ada function can return an array - or a record.
There can be a knack to using it, though. For example, if you are assigning the return value to a variable, the variable must be exactly the right size to hold the array, and there are two common ways of achieving that.
1) Fixed size array - cleanest way is to define an array type, e.g.
type Vector is new Array(1..3) of Integer;
function Unit_Vector return Vector;
A : Vector;
begin
A := Unit_Vector;
...
2) Unconstrained array variables.
These are arrays whose size is determined at runtime by the initial assignment to them. Subsequent assignments to them will fail unless the new value happens to have the same size as the old. The trick is to use a declare block - a new scope - so that each assignment to the unconstrained variable is its first assignment. For example:
for i in 1 .. last_file loop
declare
text : String := Read_File(Filename(i));
-- the size of "text" is determined by the file contents
begin
-- process the text here.
for j in text'range loop
if text(j) = '*' then
...
end loop;
end
end loop;
One warning : if the array size is tens of megabytes or more, it may not be successfully allocated on the stack. So if this construct raises Storage_Error exceptions, and you can't raise the stack size, you may need to use access types, heap allocation via "new" and deallocation as required.
Yes, an Ada function can return an array. For example, an Ada String is "A one-dimensional array type whose component type is a character type." Several of the functions defined in Ada.Strings.Fixed—including Insert, Delete, Head, Tail and Trim—return a String.

Passing an Array allocated from a C-routine to Ada

Passing arrays of structures/records from Ada to C routines is one thing. In this case the memory management is done in Ada. But when interfacing with third party libraries there is often the problem that Memory management is done in the C part.
For example: For the C-structure:
typedef struct _MYREC
{
int n;
char *str;
} MYREC;
the following C-routine allocates Memory and returns a pointer to a MYREC-array with n elements:
MYREC * allocMyrec(int n);
Problem is that the returned Pointer does not contain size-information which is required for memory-safe computation in Ada.
in Ada I would like to use the corresponding Array-Definition for the (MYREC *) pointer:
type MYREC_Array is array (Int range <>) of aliased MYREC;
pragma Convention (C, MYREC_Array);
How would a corresponding (size-awawre) Ada-Function allocMyrec look like or what would be the right strategy?
B.t.w. for one element it is possible to map the C-pointer to access MYREC . That is what the Gnat-Binding generator does. But this is not helpful.
Hints highly appreciated.
I finally got it working using the package Interface.C.Pointers, it is quite easy, here is the code :
with Interfaces.C, Interfaces.C.Pointers ;
use Interfaces.C ;
with Ada.Text_IO ; -- To Check
procedure MYRECTest is
package IIO is new Ada.Text_IO.Integer_IO (Int) ;
type PChar is access all Char ;
type MYREC is record
N : Int ;
Str : PChar ;
end record ;
pragma Convention(C, MYREC);
DefaultMyrec : MYREC := (0, null) ;
type MYREC_Array is array (Int range <>) of aliased MYREC ;
pragma Convention(C, MYREC_Array); -- Not sure if useful...
-- Here is the trick
package PMYREC is new Interfaces.C.Pointers (Int, MYREC, MYREC_Array, DefaultMyrec) ;
function AllocMyrec (N : in Int) return PMYREC.Pointer ;
pragma Import (C, AllocMyrec, "allocMyrec");
P : PMYREC.Pointer := AllocMyrec(5) ;
StartP : PMYREC.Pointer := P ; -- Initial pointer
A : MYREC_Array(0..4) := PMYREC.Value(P, 5) ; -- Here is a copy
begin
for I in A'Range loop
-- Real access:
IIO.Put(P.all.N) ;
P.all.N := P.all.N + 3 ; -- Here you're really accessing the allocated memory, not just a copy
PMYREC.Increment(P) ;
-- 'Fake' access:
IIO.Put(A(I).N) ;
A(I).N := A(I).N + 3 ; -- Here you're accessing a copy, so the modification is not made on the allocated memory
end loop ;
Ada.Text_IO.New_Line ;
end MYRECTest ;
I tested the above code setting the n attribute of all _MYREC elements in the C function to 42 + i and printing it in the Ada body, it worked (I got 42, 43, 44, 45, 46). The C function looked like (for test) :
typedef struct _MYREC {
int n ;
char *str ;
} MYREC ;
MYREC * allocMyrec (int n) {
MYREC *res = malloc(n * sizeof(MYREC)) ;
int i ;
for (i = 0 ; i < n ; i++) {
res[i].n = 42 + i;
}
return res ;
}
The DefaultMyrec variable is useless but necessary for the package creation. I assumed you always use the Value function with the length parameter to retrieve value.
More information about the package : http://www.adaic.org/resources/add_content/standards/05rm/html/RM-B-3-2.html
EDIT: The original code was making a copy of the memory pointed by P so if you update anything in the array A it won't be change in the memory allocated. In fact you should directly use the pointer P like shown in the edited code.
EDIT: I used a 'stupid' access all Char for the str attribute of MYREC but you can (and should) use almost the same stuff from Interfaces.C.Pointers if necessary. I tested it but did not want to put it in the answer because it did not add anything to it.
Sorry, I don't think there's a general solution. If you declare a type that's an access to MYREC_Array, e.g.
type MYREC_Array is array (Int range <>) of aliased MYREC;
type MYREC_Array_Access is access all MYREC_Array;
I think the GNAT compiler will expect that this points to data containing the bounds immediately followed by the data. And you're not going to get the C allocMyrec to leave space for the bounds, unless you have access to the C code and can write a wrapper that does leave space for the bounds. But to do this, you'd have to know exactly how the GNAT compiler works, and the answer will be tied to that particular implementation. If there's some special declaration that would tell GNAT to keep the bounds separate, I'm not aware of it.
Some compilers might be able to handle this; e.g. Irvine Compiler's Ada compiler keeps the bound information as part of the MYREC_Array_Access type, not contiguous with the data. (There are advantages and disadvantages to this approach.) For a compiler like that, you could construct a pointer that has the bounds you want and points to the data returned by allocMyrec. But doing so requires using unchecked operations and is highly implementation-specific.
In some cases you could do something like this:
procedure Do_Some_Work (Size : Integer) is
subtype MYREC_Array_Subtype is MYREC_Array (1 .. Size);
type MYREC_Array_Access is access all MYREC_Array_Subtype;
function allocMyrec (Size : Interfaces.C.int) return MYREC_Array_Subtype;
pragma Import (C, allocMyrec);
begin
...
Now, since the bounds are built into the subtype, they don't need to be stored in memory anywhere; so this will work, and any time you refer to an element of the array returned by allocMyrec, the compiler will make sure the index is in the range 1..Size. But you won't be able to use this result outside Do_Some_Work. You won't be able to convert the access object to any other access type defined outside Do_Some_Work. So this is a rather limited solution.
EDIT: I was assuming you wanted an Ada access object that points to the array; if not, then Holt's answer is a good one. Sorry if I misunderstood what you were looking for.
This is another answer for what you asked in the preview answer's comments (was too long and too different from the previous answer to only make an edit).
So your C code looks like:
typedef struct { ... } MYREC ;
MYREC *last_allocated ;
// Allocate an array of N MYREC and return N.
// The allocated array is "stored" in last_allocated.
int alloc_myrec (void) { ... }
MYREC* get_last_allocated (void) {
return last_allocated ;
}
Then your Ada body:
procedure MYREC_Test is
type MYREC is record
...
end record ;
pragma Convention(C, MYREC) ;
-- Global and unconstrained array
type MYREC_Array is array (Int range <>) of aliased MYREC ;
pragma Convention(C, MYREC_Array);
begin
declare
-- Allocate and retrieve the array
Size : Int := AllocMyrec ;
subtype MYREC_Array_Subtype is MYREC_Array (1 .. Size);
type MYREC_Array_Access is access all MYREC_Array_Subtype;
function GetAlloc return MYREC_Array_Access;
pragma Import (C, GetAlloc, "get_last_alloc");
MyArray : MYREC_Array_Access := GetAlloc ;
begin
-- Do whatever you want with MyArray
end ;
end ;
As I said in the previous comment, it's a bit ugly to work that way. The Interfaces.C.Pointers package is mean to do what you want to do and will be easier to use if you put everything you need in a package.
type MYREC is record
n: Integer;
str: System.Address;
end record with Convention => C;
This record contains data in an "unpinned" format, i.e. you don't use it directly. Instead, whenever you need to work with data, you must pin them:
declare
Pinned_MYREC : String (1 .. MYREC_Value.n)
with Import, Address => MYREC_Value.str;
begin
-- work with Pinned_MYREC
end;
This task can be automated using closures (or generics).
procedure Query_MYREC
(MYREC_Value : MYREC;
Process : not null access procedure (Pinned_MYREC : String))
is
Pinned_MYREC : String (1 .. MYREC_Value.n)
with Import, Address => MYREC_Value.str;
begin
Process.all (Pinned_MYREC);
end Query_MYREC;
And, often enough (when pragma Pack is not applied to access type), you can construct fat pointer type in a system-dependent way. Not a rocket science.
In my experience I had problems with fat pointer being not fat enough. In GNAT its second pointer points to bounds, and so they have to be allocated somewhere. So these custom crafted fat pointers can only reside within some container that provides a storage for bounds. And maybe patches fat pointer's second half to new bounds location when Adjust on new location happens.
In this project I provide type-safe string views and editors for direct Unbounded_String access; works on GNAT for Linux x86-64. In my project container is limited, so no Adjust needed. In my project fat pointers are access discriminants, so they can't leak.

Compatibility of the tricky dynamic arrays with dynamic arrays

Considering the old trick to make an array
Type
IntArray = Array Of Integer;
PIntArray = ^IntArray
PTDynIntArray = ^TDynIntArray;
TDynIntArray = Array[0..0] Of Integer;
{later...}
GetMem(APTDynIntArray,100*SizeOf(Integer));
APTDynIntArray^[49] := 50
Is There a way to make this tricky array compatible with a standard dynamic array ?
For example, If I want to translate an old (lets say from 1999) unit with
Procedure DoSomething(Data: PTDynIntArray);
And considering that the data will be processed using the above syntax (dataname-dereference-index in brackets), Delphi compiler will not stop if I pass a PIntArray as argument, however I get an AV at run-time (I guess that Delphi considers, in this case, that PIntArray Is the same as PTDynIntArray)
So can these two types (PIntArray and PTDynIntArray) be combined, type casted, inter-changed ? How ?
You can convert an IntArray (note: not PIntArray) to a PTDynIntArray. The reverse is not generally possible.
An IntArray is stored as a pointer to the first element of the array. The array is preceded by information about the array's length and such, but if your procedure only accesses the array elements, they won't do any harm.
You may, to be explicit, also write it as #IntArray[0].

Resources