The main reference is the Barry Kelly article: An ugly alternative to interface to object casting.
Here is a re-factored version, as included in our
SQlite3Commons.pas
unit. It will use the as
operator
for newer versions, but use fast pascal code for compilers which do not support
this syntax.
{$ifndef ISDELPHI2010} type TObjectFromInterfaceStub = packed record Stub: cardinal; case integer of 0: (ShortJmp: shortint); 1: (LongJmp: longint) end; PObjectFromInterfaceStub = ^TObjectFromInterfaceStub; {$endif}
/// low-level function to retrieve the class instance implementing a given // interface function ObjectFromInterface(const aValue: IInterface): TObject; {$ifdef HASINLINE}inline;{$endif} begin if aValue=nil then begin result := nil; exit; end; {$ifdef ISDELPHI2010} result := aValue as TObject; // slower but always working {$else} with PObjectFromInterfaceStub(PPointer(PPointer(aValue)^)^)^ do case Stub of $04244483: result := pointer(PtrInt(aValue)+ShortJmp); $04244481: result := pointer(PtrInt(aValue)+LongJmp); else result := nil; end; {$endif} end;
I suspect you'll find the resulting code more easy to read (perhaps not to understand), than the original.
Of course, we added support to out TInterfacedObjectFake
fake class used to implement
Interface-based services in mORMot, as such:
with PObjectFromInterfaceStub(PPointer(PPointer(aValue)^)^)^ do case Stub of // address of VMT[0] entry, i.e. QueryInterface $04244483: result := pointer(PtrInt(aValue)+ShortJmp); $04244481: result := pointer(PtrInt(aValue)+LongJmp); else if Stub=PCardinal(@TInterfacedObjectFake.FakeQueryInterface)^ then result := TInterfacedObjectFake(pointer(aValue)).SelfFromInterface else result := nil; end;
In its implementation, the as
operator is able to find out
the object implementing our TInterfacedObjectFake
"virtual
class".
In all cases, above code is very efficient, and will be easy to maintain.