You are not logged in.
Pages: 1
Perhaps Rtti.RegisterFromText() can be used for this.
Somewhat related to this topic, and confirms, that old style TP "object" could be very useful, and should not be deprecated. Some time ago I found, that "object"s do support interfaces, at least at syntax level. I posted it as a comment, but I think, only Mr. Pluimers have read it:
https://wiert.me/2014/02/20/hidden-feat … these-too/
Look at the declarations here. The implementation is in the blog comment...That is poor and not very interesting.
type
TInterfacedBase = object // Not CLASS
private
FInterfaceTable: Pointer;
FRefCount: Integer;
public
constructor Init;
destructor Done;
function GetInterface: IInterface;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
ITest = interface
function Foo: string;
end;
// TTest = object(ITest) // Must have an ancestor OBJECT type or gives a syntax error
TTest = object(TInterfacedBase, ITest)
public
constructor Init;
function Foo: string;
end;Sorry. Don't waste your valuable time with it.
It seems to me unrelated to Mormot, it is a reproducible IDE bug:
When opening "Project02Server.dpr", a "Project02Server.dproj" file is automatically generated by the IDE,
and it contains a specific line that contains "mormot.core".
<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;mormot.core;$(DCC_Namespace)</DCC_Namespace>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_E>false</DCC_E>
<DCC_F>false</DCC_F>
<DCC_K>false</DCC_K>
<DCC_N>false</DCC_N>
<DCC_S>false</DCC_S>
<DCC_ImageBase>00400000</DCC_ImageBase>
<SanitizedProjectName>Project02Server</SanitizedProjectName>
<VerInfo_Locale>1038</VerInfo_Locale>
<VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=</VerInfo_Keys>
<DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;mormot.core;$(DCC_Namespace)</DCC_Namespace>
<Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
<Icns_MainIcns>$(BDS)\bin\delphi_PROJECTICNS.icns</Icns_MainIcns>
</PropertyGroup>
Compilation fails with error message: "[dcc32 Fatal Error] mormot.core.variants.pas(27): F2047 Circular unit reference to 'mormot.core.base'".
I found, that it is caused by the "Unit scope names" project option: "System;Xml;Data;Datasnap;Web;Soap;mormot.core".
After deleting "mormot.core", it compiles. I don't know why, so I reported the problem, but don't know wether it is a valid solution.
I played a lot with Your nice IAutoFree idea, and finally got to a different implementation, currently for test purposes, working only with a single object instance.
I really can't decide wether this is cool or fool, so I woudld be appreciated on any comment.
// TAutoFreeData - useful for simple classes that do not have constructor and destructor ie. for emulating pascal record behavior with property access syntax.
type
// Memory signature on the heap
PAutoFreeRec = ^TAutoFreeRec;
TAutoFreeRec = packed record
AutoFree: packed record
ClassType: TClass;
RefCount: Integer;
Unknown: {array[0..0] of} Pointer; // TODO: What is this? Actually initialized by TAutoFreeData.InitInstance().
end;
Instance: packed record
ClassType: TClass;
// Data: array[1..Instance.ClassType.InstanceSize - SizeOf(Instance.ClassType)] of Byte;
end;
end;
// Implements the reference counted interface
TAutoFreeData = class(TInterfacedObject)
public
destructor Destroy; override;
end;
// Just for accessing RefCount
TAutoFreeInfo = class
private
function getAutoFree: TAutoFreeData;
public
property AutoFree: TAutoFreeData read getAutoFree;
end;
destructor TAutoFreeData.Destroy;
begin
//* Cleanup aClassType instance
// MUST NOT call Instance.Destroy, since
// it is not directly allocated on the heap, and destructor finally calls System._FreeMem()
TObject(@PAutoFreeRec(Self)^.Instance).CleanupInstance;
//* Destroy AutoFree instance and Free allocated memory
inherited;
end;
function TAutoFreeInfo.getAutoFree: TAutoFreeData;
begin
Result := TAutoFreeData(@PAutoFreeRec(Integer(Self) - SizeOf(PAutoFreeRec(nil)^.AutoFree))^.AutoFree);
end;
function AutoFreeData(var aLocalVar; aClassType: TClass): IInterface;
var
Size: integer;
begin
//* Allocate space on heap for TAutoFreeData and aClassType instance
Size := TAutoFreeData.InstanceSize + aClassType.InstanceSize;
GetMem(Pointer(aLocalVar), Size);
//* Fill only Instance.Data with zeroes
// FillChar(Pointer(aLocalVar)^, Size, 0);
FillChar(PAutoFreeRec(Integer(aLocalVar) + SizeOf(TAutoFreeRec))^, Size - SizeOf(TAutoFreeRec), 0);
//* Initialize AutoFreeData instance and return interface reference
Result := TAutoFreeData(TAutoFreeData.InitInstance(TAutoFreeData(aLocalVar)));
//* Initialize aClass instance
//* Not necessary to call aClassType.InitInstance on constructorless simple classes
// aClassType.InitInstance(TObject(@PAutoFreeRec(aLocalVar)^.Instance));
PAutoFreeRec(aLocalVar)^.Instance.ClassType := aClassType;
//* Set aLocalVar
Pointer(aLocalVar) := @PAutoFreeRec(aLocalVar)^.Instance;
end;
type
// Not mandatory to inherit from TAutoFreeInfo. It is just for test.
TTestRec28C = class(TAutoFreeInfo)
private
fID: Integer;
fName: string;
published
property ID: Integer read fID write fID;
property Name: string read fName write fName;
end;
// Inheritance and virtual functions will work
TTestRec28VC = class(TTestRec28C)
function getCalculated: string; virtual;
published
property Calculated: string read getCalculated;
end;
function TTestRec28VC.getCalculated: string;
begin
Result := Format('ID: %d, Name: %s', [ID, Name]);
end;
procedure Test28;
var
d1, d2: IInterface;
c: TTestRec28C;
sl: TStringList;
procedure Log(const Fmt: string; const Params: array of const);
begin
sl.Add(Format(Fmt, Params));
end;
begin
sl := TStringList.Create;
d1 := AutoFreeData(c, TTestRec28VC);
Log('c.ClassName: %s', [c.ClassName]);
Log('c.AutoFree.ClassName: %s', [c.AutoFree.ClassName]);
Log('c.AutoFree.RefCount: %d', [c.AutoFree.RefCount]);
c.ID := 1;
c.Name := 'First';
Log('c.ID: %d', [c.ID]);
Log('c.Name: %s', [c.Name]);
Log('c.Calculated: %s', [(c as TTestRec28VC).Calculated]);
d2 := d1;
Log('c.AutoFree.RefCount: %d', [c.AutoFree.RefCount]);
sl.SaveToFile(IncludeTrailingPathDelimiter(ExtractFileDir(Application.ExeName)) + 'Test28.txt');
sl.Free;
end;
Pages: 1