Board index » delphi » Please help me with passing a Dynamic Array to a C++ routine

Please help me with passing a Dynamic Array to a C++ routine

Hi all,

I've used Delphi for years, but don't use Dynamic Arrays much, so I'm kind
of stuck on how to pass one through to a C++ routine.  Say I have
declarations like

type
    PBlah = ^TBlah;
    TBlah =
    record
        <snip>
    end;

Then the C++ routine (in a DLL) I am trying to call expects two parameters,
a pointer to a DWORD where you tell it how many elements in the array you
have allocated enough memory for, or it returns how many elements it needs
if you didn't allocate enough, and the second, a pointer to the first TBlah
record in the array.

So I tried something dumb like this:

type
    TArrayOfBlah = array of TBlah;

procedure xyz (var NumRecs: DWORD; BlahArray: TArrayOfBlah);

because I expected TArrayOfBlah to be an (unallocated) pointer to the first
TBlah (and if you do SizeOf (TArrayOfBlah) you get 4, so I figured this
confirmed what I thought), so I did something like:

var
    NumRecs, RecNo: DWORD;
    BlahArray: TArrayOfBlah;
begin
    { First get size of array we need }
    NumRecs := 0;
    xyz (NumRecs, nil);

    { Then allocate memory }
    GetMem (BlahArray, NumRecs * SizeOf (TBlah));
    try
        xyz (NumRecs, BlahArray);

        for RecNo := 0 to NumRecs - 1
            { Do something with BlahArray [RecNo] }

    finally
        FreeMem (BlahArray);
    end;
end;

I get a whole host of problems with this, then I checked a few other posts
and looked at the SetLength routine.  Is all I actually need to do:

var
    NumRecs, RecNo: DWORD;
    BlahArray: TArrayOfBlah;
begin
    NumRecs := 0;
    xyz (NumRecs, nil);

    SetLength (BlahArray, NumRecs);
    xyz (NumRecs, BlahArray);

    for RecNo := 0 to NumRecs - 1
        { Do something with BlahArray [RecNo] }
end;

If I do this, a) Does Delphi automatically free the memory or do I need to
do an explicit SetLength (BlahArray, 0); and b) Is the parameter I'm passing
to the C++ routine still a pointer to the first TBlah?  or is it a pointer
to some strange internal Delphi structure set up by SetLength (otherwise
where does Delphi store the size you allocated with SetLength so it knows
how much memory to free?)

Thanks for any help/suggestions you may have :-)

Nigel.

 

Re:Please help me with passing a Dynamic Array to a C++ routine


Quote
"The_Commander" <impl...@jerseymail.removethisbit.co.uk> wrote in message
> I've used Delphi for years, but don't use Dynamic Arrays much, so I'm kind
> of stuck on how to pass one through to a C++ routine.  Say I have
> declarations like

> type
>     PBlah = ^TBlah;
>     TBlah =
>     record
>         <snip>
>     end;

> Then the C++ routine (in a DLL) I am trying to call expects two
parameters,
> a pointer to a DWORD where you tell it how many elements in the array you
> have allocated enough memory for, or it returns how many elements it needs
> if you didn't allocate enough, and the second, a pointer to the first
TBlah
> record in the array.

> So I tried something dumb like this:

> type
>     TArrayOfBlah = array of TBlah;

> procedure xyz (var NumRecs: DWORD; BlahArray: TArrayOfBlah);
>     xyz (NumRecs, nil);

    xyz (NumRecs, BlahArray);
Local dynamic arrays are automatically initialized to zero length.

Quote
> If I do this, a) Does Delphi automatically free the memory or do I need to
> do an explicit SetLength (BlahArray, 0); and b) Is the parameter I'm
passing
> to the C++ routine still a pointer to the first TBlah?  or is it a pointer
> to some strange internal Delphi structure set up by SetLength (otherwise
> where does Delphi store the size you allocated with SetLength so it knows
> how much memory to free?)

a) Memory allocated to dynamic arrays is automatically released so long as
you use SetLength.

b) The identifier BlahArray is a pointer that points to the memory allocated
for the array, see the ObjectPascal Reference Memory Management section for
details on the structure of this memory block. Since you are passing
BlahArray by value, a copy of the contents of BlahArray (i.e. the address of
the array's memory block) is passed. If the array has a length of zero, the
value passed will be nil (zero).

Re:Please help me with passing a Dynamic Array to a C++ routine


Quote
The_Commander wrote in message

<0wKf8.8921$Ek1.1070...@news-nb00s0.nbnet.nb.ca>...

Quote
>Hi all,

>I've used Delphi for years, but don't use Dynamic Arrays much, so I'm kind
>of stuck on how to pass one through to a C++ routine.
[...]
>Then the C++ routine (in a DLL) I am trying to call expects two
parameters,
>a pointer to a DWORD where you tell it how many elements in the array you
>have allocated enough memory for, or it returns how many elements it needs
>if you didn't allocate enough, and the second, a pointer to the first
TBlah
>record in the array.

"A pointer to the first TBlah record in the array." Mark the
straight C thinking in this.

Quote
>So I tried something dumb like this:

>type
>    TArrayOfBlah = array of TBlah;

>procedure xyz (var NumRecs: DWORD; BlahArray: TArrayOfBlah);

>because I expected TArrayOfBlah to be an (unallocated) pointer to the
first
>TBlah (and if you do SizeOf (TArrayOfBlah) you get 4, so I figured this
>confirmed what I thought), so I did something like:

>var
>    NumRecs, RecNo: DWORD;
>    BlahArray: TArrayOfBlah;
>begin
>    { First get size of array we need }
>    NumRecs := 0;
>    xyz (NumRecs, nil);

>    { Then allocate memory }
>    GetMem (BlahArray, NumRecs * SizeOf (TBlah));
>    try
>        xyz (NumRecs, BlahArray);

>        for RecNo := 0 to NumRecs - 1
>            { Do something with BlahArray [RecNo] }

>    finally
>        FreeMem (BlahArray);
>    end;
>end;

Again, straight C thinking.

- Show quoted text -

Quote
>I get a whole host of problems with this, then I checked a few other posts
>and looked at the SetLength routine.  Is all I actually need to do:

>var
>    NumRecs, RecNo: DWORD;
>    BlahArray: TArrayOfBlah;
>begin
>    NumRecs := 0;
>    xyz (NumRecs, nil);

>    SetLength (BlahArray, NumRecs);
>    xyz (NumRecs, BlahArray);

>    for RecNo := 0 to NumRecs - 1
>        { Do something with BlahArray [RecNo] }
>end;

Much better. This actually looks like Pascal. I mean Delphi.
(Dynamic arrays are NOT Pascal, they're Delphi. Don't be
surprised if they give you trouble, they're allowed to.
After all, they're not standard Pascal. It would be silly,
but totally acceptable from some points of view if dynamic
arrays were implemented by a bucketload of compiler magic
that resulted in them being totally different from static
arrays and completely non-interchangeable with them as seen
from a C DLL, _as long_ as they are compatible with static
arrays within Delphi.)

Quote
>If I do this, a) Does Delphi automatically free the memory or do I need to
>do an explicit SetLength (BlahArray, 0); and b) Is the parameter I'm
passing
>to the C++ routine still a pointer to the first TBlah?  or is it a pointer
>to some strange internal Delphi structure set up by SetLength (otherwise
>where does Delphi store the size you allocated with SetLength so it knows
>how much memory to free?)

I think the value passed _is_ in fact a pointer to the first
element. The extra data is stored at the beginning of the
complete object and the pointer points halfway into it. This
same trick is used with long strings (from which dynamic
arrays borrow _very_ heavily) and objects.

It might be a good idea to pass the array by reference. That
ensures that a pointer is passed, and the pointer is more
likely to be compatible with a regular array than the data
that would be put on the stack for a real by-value call.
(More likely as in 99.9% vs 99%. It is a bit academic.)

As the array goes out of scope at the end of the procedure,
I would expect that it is deallocated. The OPLG should have
a definitive statement about that, which probably involves
the word "finalized".

Groetjes,
Maarten Wiltink

Other Threads