Conversion of pointers and arrays from C++ to Delphi
I am trying to use the ANN
(approximate nearest neighbor) library (the .dll) within my Delphi code. The library is written in C++, and although the data types are fairly simple I am having some trouble.
I used h2pas to convert the C++ header file as far as I could get it. I ended up with the following data types (C++ on the left, Delphi on the right):
enum ANNbool {ANNfalse = 0, ANNtrue = 1}; --> ANNbool = longint;
typedef double ANNcoord; --> ANNcoord = double;
typedef double ANNdist; --> ANNdist = double;
typedef int ANNidx; --> ANNidx = longint;
typedef ANNcoord* ANNpoint; --> ANNpoint = ^ANNcoord;
typedef ANNpoint* ANNpointArray; --> ANNpointArray = ^ANNpoint;
typedef ANNdist* ANNdistArray; --> ANNdistArray = ^ANNdist;
typedef ANNidx* ANNidxArray; --> ANNidxArray = ^ANNidx;
For starters I want to sucessfully port the sample included with the ANN
library. The C++ code for this sample is linked here without comments (if you want the comments, just download the library from ANN's webpage).
Even more specifically, here is the excerpt that I am having trouble with (C++ code..):
...
开发者_运维问答while (nPts < maxPts && readPt(*dataIn, dataPts[nPts])) nPts++;
...
bool readPt(istream &in, ANNpoint p)
{
for (int i = 0; i < dim; i++) {
if(!(in >> p[i])) return false;
}
return true;
}
A member on stackoverflow helped me with a partial conversion of the readPt
function - but I am not sure if it is completely correct (Delphi code..):
function readPt(inStr: TStream; p: ANNpoint): boolean;
var
Size: longint; // number of bytes to read
begin
inStr.Size := SizeOf(ANNcoord) * dim;
Result := inStr.Read(p^, Size) = Size;
end;
Here is my unsuccessful attempt at implementing that while
loop in Delphi (irrelevant code omitted):
var
dataPts: AnnPointArray;
BinStream: TMemoryStream;
dim,maxPts,nPts: LongInt;
begin
dim := 2;
maxPts := 1000;
dataPts := annAllocPts(maxPts, dim);
//GENERATE TStream data
///////////////////////////
BinStream := TMemoryStream.Create;
BinStream.SetSize(1001);
for nPts := 0 to 1000 do
begin
BinStream.Write(nPts, 1);
end;
BinStream.Position := 0
///////////////////////////
nPts:=0;
while nPts < maxPts do
begin
readPt(BinStream, dataPts[nPts]);
nPts:=nPts+1;
end;
end;
When I attempt to run this I get a segmentation fault at readPt(BinStream, dataPts[nPts]);
I will be extremely grateful if someone would take the time to help me out here!
Edit: In case the C++ function for annAllocPts()
is important.. here it is:
ANNpointArray annAllocPts(int n, int dim) // allocate n pts in dim
{
ANNpointArray pa = new ANNpoint[n]; // allocate points
ANNpoint p = new ANNcoord[n*dim]; // allocate space for coords
for (int i = 0; i < n; i++) {
pa[i] = &(p[i*dim]);
}
return pa;
}
And here is how I implemented it:
function annAllocPts(n: longint; dim: longint): ANNpointArray cdecl;
external 'ANN.dll' index 33;
Edit 2: I am still having trouble properly filling the input stream (I think)...
var
ThisDouble: Double;
begin
binstream := TMemoryStream.Create;
ThisDouble :=1.001;
for nPts := 0 to maxPts*dim do
begin
binstream.Write(ThisDouble,SizeOf(ThisDouble));
end;
BinStream.Position := 0;
nPts:=0;
while nPts < maxPts do
begin
if not readPt(BinStream, dataPts[nPts]) then
Break;
nPts:=nPts+1;
end;
Your code calls the DLL function to allocate a array of 1000 2-element arrays of Double. Then you fill a stream with 1001 bytes. You're trying to populate 16000 bytes of storage with only 1001 bytes of data.
Furthermore, the data you're putting in the stream doesn't form meaningful Double values anyway.
Also, your version of readPt
is wrong. You're assigning inStr.Size
instead of the local Size
variable. The compiler should have warned you that Size
was used without being initialized. Your code has the effect to truncating the input stream to the length of one array segment and then attempting to read an unknown number of bytes from the stream. Use the version you got from your other question.
The code in readPt() is correct. But it reads more than one double in one go, just like the C++ example in the other question.
Your problem is that you write maxPts (=1000) doubles to the memory stream, but then, in a loop (of MaxPts = 1000 iterations), you read dim (= 2) doubles per iteration (readPt reads dim ANNcoords in one go), so you try to read 1000*2 ANNcoords and run out of data before you reach the end. You should check the return value of readPt() in the loop and break if it is false:
nPts:=0;
while nPts < maxPts do
begin
if not readPt(BinStream, dataPts[nPts]) then
Break;
nPts:=nPts+1;
end;
Of course, you could also write dim * maxPts items into the memory stream.
精彩评论