I need to make parts of my Win32 Delphi app available to another company's Linux gcc program.
Throughput and deployment requirements make any sort 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.)
Related
I have a project that should compile under Delphi and FPC, with generic class procedures. They compile to Delphi and FPC.
The calls to these procedures only compile to Delphi. To FPC, a specialization of the generic types must be given.
I want to omit this specializations in the calls, because there are maybe thousands of such calls to these procedures in the project. I would have to pick out the corresponding types for each call.
However, with specializations added in the calls, everything compiles in both directions.
// a simpified example of a generic class function
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
interface
type
TMyClass = class
class function MyFunc<A, B> ( bool : boolean;
var Data : A;
var Key : B): cardinal;
end;
implementation
class function TMyClass.MyFunc<A, B>( bool : boolean;
var Data : A;
var Key : B): cardinal;
begin
// do something
end;
procedure callGen;
var
i : integer;
s : string;
begin
TMyClass.MyFunc(true,i, s);// compiles only to delphi
TMyClass.MyFunc<integer, string>(true,i, s);// compiles in both directions
end;
I want to omit the insertion of <integer, string> in the call, for in the project there are a huge number of of different types to insert and even more calls exist.
Error message in Lazarus for
TMyClass.MyFunc(true,i, s);
Fatal: Syntax error, ")" expected but "," found
I want to refactor DelphiAST to use interfaces to deal with different types, rather than the clunky
TDirectionary it uses now.
Some research shows that 70%+ of the running time is spend in the dictionary.
So I'll make interfaces like:
TSyntaxNode = class
...
end;
IIdentifier = interface
['{D72E945D-E397-4E02-B702-8AE6C235F9C6}']
function GetIdentifier: string;
property Identifier: string read GetIdentifier;
end;
IMethod = interface(Identifier)
['{8E6119DC-E0F3-42BD-A5BF-FB658E34499E}']
.....
end;
TMethodNode = class(TSyntaxNode, IMethod, IIdentifier,...)
...
end;
The problem according to Roman is:
Reference counting may cause performance issues. DelphiAST creates thousands of classes to produce the syntax tree (more than 100,000 of TSyntaxNode instances, when input file is big enough). How many times the reference counter would be called?
Every time that happens a hidden try finally is invoked and that will slow things way down.
Strict use of const in method params prevents the refcount code calling the method, but afaik it still happens every time you do something like, say, MyRef = List[0] - it will increment the refcount assigning to MyRef, even though the item is still present in the list.
How can I work with interfaces whilst not having to worry about refcounting and try-finally blocks?
I'm perfectly happy to manage destruction of classes manually.
Further info
I'm guessing I need to use TAggregatedObject as a base ancestor.
And I read somewhere that not assigning a GUID inhibits reference counting, but have to source to back that up.
However losing the GUID's would lead to problems in obtaining sub-interfaces so I'd have to devise a solution to that....
Can I use interfaces without invoking hidden try-finally's?
No. The compiler emits reference counting code with interfaces no matter what. You cannot avoid it.
You can implement you own version of interfaces using a record of function pointers. It will be more clunky but will avoid heap allocation and reference counting.
"Thousands of objects" always gives me a shiver. There is a significant overhead to an object in memory. You forget about it, but it pops up again when you're trying to manage thousands, or notice you loose performance on it, or start to try writing or reading from file...
Using interfaces won't change much as far as I can tell, since you still use objects (class instances) underneath.
Endeavours of this magnitude require specific use of good-old straight-to-memory data-structures. For example I've been playing with an AST stored in an array of records: https://github.com/stijnsanders/strato
Yes No you cancannot use interfaces without invoking try-finally's and refcounting.
You can however greatly reduce the number of hidden exception handlers.
You just have to be really careful to do two things.
Always use const parameters when passing interfaces.
Never store the interface in an interface type variable, but use a homebrew record to encapsulate the interface so that its refcount will not be touched.
Here's a sample of the encapsulating record:
type
TInterface<Intf: IInterface> = record
private
P: Pointer;
public
function I: Intf; inline;
class operator Implicit(const A: Intf): TInterface<Intf>; inline;
end;
function TInterface<Intf>.I: Intf;
begin
pointer(IInterface(Result)):= P;
end;
class operator TInterface<Intf>.Implicit(const A: Intf): TInterface<Intf>;
begin
Result.P:= pointer(IInterface(A));
end;
Here's a sample program to demonstrate the concept.
program Project32;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TInterface<Intf: IInterface> = record
private
P: Pointer;
public
function I: Intf; inline;
class operator Implicit(const A: Intf): TInterface<Intf>; inline;
end;
ITest1 = interface
function Test1: integer;
end;
ITest2 = interface
function Test2: integer;
end;
TTest = class(TAggregatedObject, ITest1, ITest2)
function Test1: integer;
function Test2: integer;
end;
{ TTest }
function TTest.Test1: integer;
begin
Result:= 1;
end;
function TTest.Test2: integer;
begin
Result:= 2;
end;
{ TInterface<Intf> }
function TInterface<Intf>.I: Intf;
begin
pointer(IInterface(Result)):= P;
end;
class operator TInterface<Intf>.Implicit(const A: Intf): TInterface<Intf>;
begin
Result.P:= pointer(IInterface(A));
end;
var
I1: TInterface<ITest1>;
I2: TInterface<ITest2>;
Test: TTest;
begin
Test:= TTest.Create(nil); //Force AV on _AddRef, _Release
If (Test.Test1 = 1) then WriteLn(S);
I1:= Test;
If (I1.I.Test1 =1) then WriteLn(S);
I2:= Test;
If (I2.I.Test2 = 2) then WriteLn(S);
ReadLn(s);
Test.Free;
end.
The TAggregatedObject does not have a interface to handle the _AddRef/_Release calls.
During the lifetime of the program, no problems will occur, however Delphi does wrap the creation of TTest in a try-finally which will generate an exception when exiting the function.
In real-world use you'd have to use a TInterfacedObject. If you pass the interface references around a lot it might help though.
I have a list of generics in which I want to put either some records or some classes
TMyList<T> = class
private
fCount: Cardinal;
fItems: array of T;
public
constructor Create(aSize: Integer);
procedure UpdateItem(const x: T);
end;
But I can't have the compiling
procedure TMyList<T>.UpdateItem(const x: T);
var
I: integer;
begin
for I := 0 to fCount - 1 do
if fItems[I] = x then begin // <- error E2015
//do update
break;
end;
end;
It works for classes with this declaration: TMyList<T : class> = class, but then it can't hold records anymore.
Of course, for the record I declare class operator Equal(Left, Right : TMyRecord) : Boolean; so that MyRecord1 = MyRecord2 would compile.
This can never be made to work using the = operator. The reason is that generic constraints are not rich enough to specify the availability of operators. You simply cannot use the = operator on generic operands.
You can do so if you constrain the operands to be a class, because a classes are references, and the compiler knows how to compare references for equality. Basically the compiler needs to know how to generate the code when it compiles the generic class. Unlike C++ or Smalltalk templates, with generics the compiler does not wait until instantiation to compile the code.
If you want to use a custom comparer then you will need to supply that explicity. Which is rather frustrating, I know. If you can make do with the default comparer you can use:
TEqualityComparer<T>.Default
Given a text string containing a type name, is there some way to get the appropriate type itself?
I'm looking to do something like this:
type
TSomeType<T> = class
// yadda yadda
end;
procedure DoSomething;
var
obj : TObject;
begin
o := TSomeType<GetTypeByName('integer')>.Create;
// do stuff with obj
end;
I've looked at several RTTI explanations online and looked through the Delphi units and don't see what I'm looking for. Is this possible?
No, generics are entirely compiletime.
The new RTTI unit in Delphi 2010 has a way of retrieving types declared in the interface section of units. For any given type, represented by a TRttiType instance, the TRttiType.QualifiedName property returns a name that can be used with TRttiContext.FindType later to retrieve the type. The qualified name is the full unit name (including namespaces, if they exist), followed by a '.', followed by the full type name (including outer types if it nested).
So, you could retrieve a representation of the Integer type (in the form of a TRttiType) with context.FindType('System.Integer').
But this mechanism can't be used to retrieve instantiations of generic types that weren't instantiated at compile time; instantiation at runtime requires runtime code generation.
You can always register your types into some sort of registry (managed by a string list or dictionary) and create a factory function to then return the appropriate object. Unfortunately you would have to know in advance what types you were going to need. Something similar to the Delphi functions RegisterClass and FindClass (in the classes unit). My thinking is to put the generic template type into the list directly.
An example of possible usage:
RegisterCustomType('Integer',TSomeType<Integer>);
RegisterCustomType('String',TSomeType<String>);
if FindCustomType('Integer') <> nil then
O := FindCustomType('Integer').Create;
EDIT: Here is a specific simple implementation using a tDictionary from Generics.Collections to handle the registry storage...I'll leave extracting this into useful methods as a simple exercise for the reader.
var
o : TObject;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) then
ShowMessage(o.ClassName);
end;
Another EDIT: I was giving this some thought last night, and discovered another technique that you can merge into this concept. Interfaces. Here is a quick do nothing example, but can easily be extended:
TYPE
ITest = interface
['{0DD03794-6713-47A0-BBE5-58F4719F494E}']
end;
TIntfList<t> = class(TList<T>,ITest)
public
function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
procedure TForm1.Button7Click(Sender: TObject);
var
o : TObject;
fTestIntf : ITest;
begin
TypeDict := TDictionary<String,TClass>.Create;
TypeDict.Add('integer',TIntfList<integer>);
if TypeDict.ContainsKey('integer') then
o := TypeDict.Items['integer'].Create;
if Assigned(o) and Supports(o,ITest,fTestIntf) then
ShowMessage(o.ClassName);
end;
of course you would have to implement the QueryInterface, _AddRef and _Release methods and extend the interface to do something more useful.
If you forget generics and basic types, the "RegisterClass" function would be helpful. But it doesn't work for generics or basic types.
EDIT:
Is there a better way of doing this ?
TPendingBets = class(TDataModule)
private
public
function GetBdy(out IdEvent : Integer ) : Boolean; overload;
function GetBdy(out IdEvent : Integer; out idBetType : TBetTypes) : Boolean; overload;
function GetBdy(out IdEvent : Integer; out idBetType : TBetTypes; Out TotalOrgStake,Price : Double; out PriceError :Boolean): Boolean; overload;
end;
implementation
////////////////////
function TPendingBets.GetBdy(out IdEvent : Integer ): Boolean;
var idBetType : TBetTypes;
TotalOrgStake,Price : Double;
PriceError :Boolean;
begin
result := GetBdy(IdEvent,idBetType,TotalOrgStake,Price,PriceError);
end;
////////////////////
function TPendingBets.GetBdy(out IdEvent : Integer; out idBetType : TBetTypes): Boolean;
var TotalOrgStake,Price : Double;
PriceError :Boolean;
begin
result := GetBdy(IdEvent,idBetType,TotalOrgStake,Price,PriceError);
end;
////////////////////
function TPendingBets.GetBdy(out IdEvent : Integer; out idBetType : TBetTypes; Out TotalOrgStake,Price : Double; out PriceError :Boolean): Boolean;
begin
result := false;
if cdBdy.Eof = False then
begin
IdEvent := cdBdy.FieldByName(IdEvent ).AsInteger);
idBetType := TBetTypes(cdBdy.FieldByName(idBetType ).AsInteger);
//.
//.
result := True;
end;
end;
NOTE:
As my initial question was not very clear I have removed part of it
Overloading does not mean having 5 copies of the code in the function. You might have 5 functions, but 4 of them just call the main function with the correct parameters.
I generally don't like out parameters. Have you considered returning a record containing all the results?
Fn1Result = record
A: Extended;
B: Integer;
C, D, E, F: Extended
S: String;
end;
Then declare your function like:
function Fn1: Fn1Result;
begin
Fn1.A := C_ConstA;
// etc. . . .
end;
Naturally I am assuming you are not using output parameters as input. Per the Delphi help:
An out parameter, like a variable parameter, is passed by reference. With an out parameter, however, the initial value of the referenced variable is discarded by the routine it is passed to. The out parameter is for output only; that is, it tells the function or procedure where to store output, but doesn't provide any input.
I get a funny feeling you are using output parameters as input parameters. Don't. Pass them as var if you want them to go both ways.
I think the overload solution is quite a good solution, at least it is one. Other programming languages require you to declare functions with different names.
Another approach may be a dynamic array parameter like:
type
TExtendedDynArray = array of Extended; // if not already declared elsewhere
function Fn1(out arr: TExtendedDynArray): Boolean;
In addition to what Uwe wrote (and I agree that overloading is a better solution here; up voted), you should never presume an out parameter to have any value at all at the start of the function. That's what out means.
You can use overloaded function something like this
function Fn1(Out A:string; B:integer=5) : boolean; overload;
function Fn1(Out A:string) : boolean; overload;
you must to define both functions.
As Jim showed, you don't need to copy the code around when overloading.
I don't like much to mix parameters with default values and overloading, because (doing it without care) you can create a mess.
Normally, if I have to support a lot of syntaxes:
1) I define a complete signature, with ALL PARAMETERS. Let's call it the
'master' one.
2) All overloads call that one. Or call one to another, but in the end every
overload chain must call the 'master' one to execute the task.
For me, overloads are "parameter crunchers" or "translators". Later, if I need
something to modify the master behavior, I create new parameters on 'master' (which can have default values - in fact, mostly have).
If I need a overload with the new parameter, I create a new one and it simply pass it to
the "master" procedure. The side effect is that the old ones continue to behave the same.
Default values can only be used for input parameters (that is: By value and const). For var and out parameters, default values are not possible.
As Jim already pointed out: You can avoid duplicating the code by having most overloaded functions call the one which takes all parameters, so they are merely thin layers over the original function. From Delphi 2005 on you can declare them inline, so the compiler might actually generate more efficient code.