Reputation: 1205
I have following problem:
For the child class the interface table is not generated at all.
type
ITest = interface
['{69068A88-6712-40E0-B1E3-DA265F7428EA}']
procedure Test;
end;
TBase = class(TInterfacedObject, ITest)
protected
procedure Test; virtual;
public
constructor Create;
end;
TChild = class(TBase)
protected
procedure Test; override;
end;
constructor TBase.Create;
begin
Assert(GetInterfaceTable <> nil);
end;
so when using following construction:
var
X: ITest;
begin
X := TChild.Create;
end;
I get the assertion failed.
So I do know that I need to re-declare the interfaces in the class declaration to fix this problem. But is that a language feature or an compiler old-old issue?
Because at compile time the compiler knows that the TChild is implementing the ITest interface. But once we go to run-time I do need to make duplicate re-declaration of the interfaces from the base! Why should we do that? For me it looks buggy.
Upvotes: 3
Views: 264
Reputation: 43033
It is as designed.
AFAIR GetInterfaceTable
is a very low-level method, which works only at a given class level. You should not have to use this method in your code, unless you are messing with the low-level RTTI information... but in all cases, you should better not use it.
Here how it is implemented:
class function TObject.GetInterfaceTable: PInterfaceTable;
begin
Result := PPointer(PByte(Self) + vmtIntfTable)^;
end;
So you would have to check the parent classes types, too, using a recursive call or a loop.
For instance, here is a sample of its use in System.pas:
class function TObject.InitInstance(Instance: Pointer): TObject;
var
IntfTable: PInterfaceTable;
ClassPtr: TClass;
I: Integer;
begin
FillChar(Instance^, InstanceSize, 0);
PPointer(Instance)^ := Pointer(Self);
ClassPtr := Self;
while ClassPtr <> nil do
begin
IntfTable := ClassPtr.GetInterfaceTable;
if IntfTable <> nil then
for I := 0 to IntfTable.EntryCount-1 do
with IntfTable.Entries[I] do
begin
if VTable <> nil then
PPointer(@PByte(Instance)[IOffset])^ := VTable;
end;
ClassPtr := ClassPtr.ClassParent;
end;
Result := Instance;
end;
Pretty low-level stuff, isn't it?
To implement an IoC pattern, you would rather have to use TObject.GetInterfaceEntry()
, which does what you expect.
Upvotes: 1
Reputation: 28516
As documented GetInterfaceTable returns list of interface entries for the class. In your case that is TChild
that does not have any interfaces implemented by itself.
Returns a pointer to a structure containing all of the interfaces implemented by a given class.
GetInterfaceTable returns the interface entries for the class. This list contains only interfaces implemented by this class, not its ancestors. To find the ancestor list, iteratively call ClassParent and then call GetInterfaceTable on the value it returns. To find the entry for a specific interface, use the GetInterfaceEntry method instead.
GetInterfaceTable
is class function, just like ClassName
is class function. It depends on instance class not on from which part of code you have called it:
If you run following code it will give you different ClassName
regardless of the fact you are calling code in TBase
constructor.
constructor TBase.Create;
begin
writeln(ClassName);
end;
var
x : ITest;
X := TBase.Create; // outputs TBase
X := TChild.Create; // outputs TChild
Upvotes: 2