Assign a variable a value from an array - pascal - arrays

I have an array and I wish to assign the value of part of the array to a string, however, I get an error:
Left side cannot be assigned to
when I do this:
(s):=(Array[1]);
Does anyone know how I can resolve this problem ?

This is a feature (or bug?) of all 32/64-bit Delphi versions I tested. Even a minimal complete program like
program test;
var
s: string;
begin
(s) := 'a';
writeln(s);
end.
will not compile without this error. No problems with Turbo/Borland, Virtual, or Free Pascal (even in Delphi mode). I assume that the Delphis treat (s) as an expression, which can have a value but you cannot assign a value to it.
Of course if you write s := 'a'; Delphi works as expected.
BTW: I consider it a bad habit, that you put superfluous () around variables and expressions.

Sorry for bumping an old thread here, but I believe that you should first declare your array.
If the variable Array is considered a string then the output of Array[1] would be the first character of that string.

Try this code ....
Var
myVar : Integer;
myArray : Array[1..5] of Integer;
Begin
myArray[2] := 25;
myVar := myArray[2];
End.
Reference: http://pascal-programming.info/lesson10.php

Related

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

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.

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].

Get string return value from C DLL in Delphi

I have a legacy DLL written in C that contains a function that returns a string, and I need to access this function from Delphi. The only info I have about the DLL is the VB declare for accessing the function:
Public Declare Function DecryptStr Lib "strlib" (Str As String) As String
I've tried the following without success:
Declaration:
function DecryptStr(s: PChar): PChar; cdecl; external 'strlib.dll';
Usage:
var
p1, p2 : pchar;
begin
GetMem( p1, 255 );
StrPCopy( p2, 'some string to decrypt' );
p1 := DecryptStr( p2 );
end;
This consistently crashes the DLL with an Access Violation. I'm at a loss.
Any suggestions ?
Consider rewriting your test code as follows:
var
p1, p2 : pchar;
begin
GetMem( p1, 255 ); // initialize
GetMem( p2, 255 );
StrPLCopy( p2, 'some string to decrypt', 255 ); // prevent buffer overrun
StrPLCopy( p1, DecryptStr( p2 ), 255); // make a copy since dll will free its internal buffer
end;
If still fails within a call to DecryptStr, then read http://support.microsoft.com/kb/187912 carefully.
p2 isn't initialized. StrPCopy copies the string to a random memory location. And most likely the calling convention is stdcall.
I'm guessing here, but are you sure it's cdecl? If the VB declare isn't mentioning it, I'd assume it's in fact a STDCALL function (STDCALL is quite common on Windows, as almost all of its native API uses it). Calling a function of one calling convention as if it were of another calling convention can really mess up the stack, usually leading to a crash.
Also, be sure to check whether the string is ANSI (LPSTR/LPCSTR) or UNICODE (LPWSTR/LPCWSTR). I don't know VB or Delphi, so I don't know what each one uses by default.
As Jozz says, p2 (where you copy your string to) is never initialized in your example.
Try this instead.
var
p1, p2 : pchar;
begin
GetMem( p2, 255 ); // allocate memory for 'some string...'
StrPCopy( p2, 'some string to decrypt' );
p1 := DecryptStr( p2 );
end;
Also, the memory you allocated by calling Getmem(p1,...) would have been leaked, because p1 was overwritten by the function return from DecryptStr.
However, I'd be a bit concerned about exactly what DecryptStr is returning, and who owns the memory pointed to by p1. If it's returning a pointer to memory allocated by the DLL you will need to be careful how that memory is freed.
I agree with CesarB, try to declare it with stdcall directive as:
function DecryptStr(s: PChar): PChar; stdcall; external 'strlib.dll';
if it doesn't work, post the VB declaration here.
The best way in these kind of situation is to debug your program and check the stack before and after executing the callback. How knows, it might even be a bug in the external DLL?
This way you will see pretty easy how to correct this.
Was the dll written in Borland C or C++Builder by any chance with the intention of being used with Delphi? In which case it could have been compiled using a pascal directive.
The suggestions that the strings must be "initialised" seem to be correct. This is because
C will require that the string being passed in is null-terminated. Check that the character in the bufffer just after the end of the text is a null (#0).
Why do you assume that the string passed in is exactly 255 chars long? You need to allocate Length(p1) + 1 bytes - for the chars in p1, and a #0 char at the end.
Also, your code sample seems confused as to the use of p1 and p2. It looks like p1 is the buffer passed to the C DLL, which you allocate, and p2 is the returned string that the DLL allocates. But then the code would be (note use of p1 and p2)
var
p1, p2 : pchar;
begin
GetMem( p1, 255 );
StrPCopy( p1, 'some string to decrypt' );
p2 := DecryptStr( p1 );
end;
Better variable names would help you make this clearer.
I'm dropping in my solution as I've sturggled quite a bit with it and haven't found it in any of the answers.
The C++ function looks like this:
int __stdcall DoSomething(char * _name);
To get it working in Delphi, I declare the following function
function DoSomething(name: PAnsiChar): integer; stdcall; external 'somedll.dll';
And then when I make the call, I have a function that looks like this:
var s: PAnsiChar;
begin
GetMem(s, 255);
DoSomething(s);
// s now contains the value returned from the C DLL
end;
I have tried using PChar instead of PAnsiChar but all I get in return is garbage. Also, if I declare the function in Delphi with the parameter set to var , I get an exception when I try to read it.
Hope this helps anyone..

Resources