I need to make parts of my Win32 Delphi app available to another company's Linux gcc program.
Throughput and deployment requirements make any s开发者_开发百科ort of remote service unsuitable so I'm looking at using FreePascal to build a .SO (Linux equivalent of a DLL) that the gcc app can call.
It's a long time since I used C/C++ and never on Linux so I'm a little unsure as to how best to structure the DLL/SO interface for compatibility with a gcc caller.
Here's a representation of my data structures
TFoo = record
x, y : double;
a : smallint;
b : string;
end;
TBar = record
a : double;
b : longint;
c : string;
end;
TFooBar = record
foo : array of TFoo;
bar : array of TBar;
end;
procedure Process(const inFooBar : TFooBar);
To make this Process method externally available via a FreePascal .SO how do I need to modify these declarations? I'm thinking something along the lines of
TFoo = record
x, y : double;
a : smallint;
b : PChar;
end;
TBar = record
a : double;
b : longint;
c : PChar;
end;
TFooBar = record
foo : ^TFoo;
foo_count : longint;
bar : ^TBar;
bar_count : longint;
end;
procedure Process(const inFooBar : TFooBar);
Am I on the right track? I don't have to get this exactly right, the other company's programmers will very likely fix up my mistakes. I just don't want them to laugh too hard when they see what I've sent them.
Use PAnsiChar
instead of PChar
, and integer
instead of longint/smallint
(since you use aligned records, there is no benefit of using a smallint field).
Define some pointer types like PFoo
and PBar
, which will be better written than ^TFoo
e.g.
If you need to access some arrays, you could define TFooArray
and TBarArray
, as in the code below.
Don't forget the cdecl
or stdcall
calling convention for any function/procedure.
type
TFoo = record
x, y : double;
a : integer;
b : PAnsiChar;
end;
TBar = record
a : double;
b : integer;
c : PAnsiChar;
end;
TFooArray = array[0..maxInt div sizeof(TFoo)-1] of TFoo;
TBarArray = array[0..maxInt div sizeof(TBar)-1] of TBar;
PBar = ^TBar;
PFoo = ^TFoo;
PFooArray = ^TFooArray;
PBarArray = ^TBarArray;
TFooBar = record
foo : PFooArray;
foo_count : integer;
bar : PBarArray;
bar_count : integer;
end;
procedure Process(const inFooBar : TFooBar); cdecl; external 'ProcessGCCLibrary.so';
Will be your process read-only?
If the C part need to add some items, you'll have to provide some memory reallocation methods to the external library, mapping at least reallocmem()
.
Note that Delphi dynamic arrays can be mapped easily into C compatible structure, as such:
type
TFooDynArray: array of TFoo;
TBarDynArray: array of TBar;
procedure CallProcess(const aFoo: TFooDynArray; const aBar: TBarDynArray);
var tmp: TFooBar;
begin
tmp.foo := pointer(aFoo);
tmp.foo_count := length(aFoo);
tmp.bar := pointer(aBar);
tmp.bar_count := length(aBar);
Process(tmp);
end;
This could make your Delphi code much more readable. Take a look at our TDynArray
wrapper if you want high-level access to such a dynamic array of records, with TList
-like methods.
Since the AnsiString
type maps a PAnsiChar
, from the binary point of view, you can even define your records as such:
type
TFoo = record
x, y : double;
a : integer;
b : AnsiString;
end;
TBar = record
a : double;
b : integer;
c : AnsiString;
end;
When mapped to the gcc application, it will be read as a regular *char
.
Using AnsiString
here you won't need to handle memory allocation from your Delphi code. With an associated dynamic array, it could make your Delphi code much easier to maintain. Note that our TDynArray
wrapper will handle nested AnsiString
in records as expected, even for the highest level methods (e.g. binary serialization or hashing).
To ensure that the record packing/alignment/padding is as GCC expects, add {$packrecords c} to your Pascal source. Note that this directive is specific to the Free Pascal Compiler, Delphi does not support it.
Looks pretty good. Thoughts:
Your arrays (declared as a pointer and count) are going to be fully manually managed - that's fine for you?
You should change:
procedure Process(const inFooBar : TFooBar);
to include a C-compatible calling convention. I'm not sure what FreePascal and GCC both support, but something like
cdecl
should work fine.You should also (for safety) specify the structure alignment / packing.
PChar
should be explicitly narrow or wide (wide is normal these days.)
精彩评论