In Delphi, IUnknown is declared as:
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
Note: The output parameter is untyped
In my TInterfacedObject descendant i need to handle QueryInterface, so i can return an object that supports the requested interface:
function TFoo.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if IsEqualGUID(IID, IFooBar) then
begin
Obj := (TFooBar.Create(Self) as IFooBar);
Result := S_OK;
end
else
Result := inherited QueryInterface(IID, {out}Obj);
end;
The problem comes on the line:
Obj := (TFooBar.Create(Self) as IFooBar);
Delphi complains:
Operator not applicable to this operand type
Obviously i don't know how or what to assign to an untyped out parameter. i can randomly try things, in hopes that the compiler will stop complaining:
Obj := TFooBar.Create(Self);
Obj := Pointer(TFooBar.Create(Self));
Obj := Pointer(TFooBar.Create(Self) as IFooBar);
Ignoring all the code i've written (if required): how do i implement QueryInterface in an object descendant from TInterfacedObject?
The real problem i've been trying to solve can be boiled down to i want to:
i want to override methods in an interface
In the same way:
TList = class(TObject)
...
function GetItem(Index: Integer): Pointer;
procedure SetItem(Index: Integer; Value: Pointer);
property Items[Index: Integer]: Pointer read GetItem write SetItem;
end;
can be overridden in a descendant class:
TStudentList = class(TList)
...
function GetItem(Index: Integer): TStudent;
procedure SetItem(Index: Integer; Value: TStudent);
property Items[Index: Integer]: TStudent read GetItem write SetItem;
end;
i want to so the same with interfaces:
IFoo = interface(IUnknown)
...
function GetItem(Index: Variant): Variant;
procedure SetItem(Index: Variant; Value: Variant);
property Items[Index: Variant]: Variant read GetItem write SetItem;
end;
IFooGuidString = interface(IFoo)
...
function GetItem(Index: TGUID): string ;
procedure SetItem(Index: TGUID; Value: string );
property Items[Index: TGUID]: string read GetItem write SetItem;
end;
Problem is that how i have to begin loading up my implementing object with:
TFoo = class(TInterfacedObject, IFoo, IFooGuidString)
public
function IFoo.GetItem = FooGetItem;
procedure IFoo.SetItem = FooSetItem;
function FooGetItem(Index: Variant): Variant;
procedure FooSetItem(Index: Variant; Value: Variant);
function IFooGuidString.GetItem = FooGuidStringGetItem;
procedure IFooGuidString.SetItem = FooGuidStringSetItem;
function FooGuidStringGetItem(Index: TGUID): string ;
procedure FooGuidStringSetItem(Index: TGUID; Value: string );
end;
And there isn't just the two methods in IFoo, there's 6. And then if i want to add another supported interface:
IFooInt64String = interface(IFoo)
...
function GetItem(Index: Int64): string ;
procedure SetItem(Index: Int64; Value: string );
property Items[Index: Int64]: string read GetItem write SetItem;
end;
TFoo = class(TInterfacedObject, IFoo, IFooGuidString)
public
function IFoo.GetItem = FooGetItem;
procedure IFoo.SetItem = FooSetItem;
function FooGetItem(Index: Variant): Variant;
procedure FooSetItem(Index: Variant; Value: Variant);
function IFooGuidString.GetItem = FooGuidStringGetItem;
procedure IFooGuidString.SetItem = FooGuidStringSetItem;
function FooGuidStringGetItem(Index: TGUID): string ;
procedure FooGuidStringSetItem(Index: TGUID; Value: string );
function IFooInt64String.GetItem = FooInt64StringGetItem;
procedure IFooInt64String.SetItem = FooInt64StringSetItem;
function FooInt64StringGetItem(Index: Int64): string ;
procedure FooInt64StringSetItem(Index: Int64; Value: string );
end;
And things get really unwieldy very fast.
You need to type-cast the left side of the assignment statement. That way, the untyped parameter has a type, and the compiler knows how to assign it a value:
IFooBar(Obj) := TFooBar.Create(Self) as IFooBar;
Please note that you're breaking one of the requirements of COM. If you query for an interface, you should be able to query the result for IUnknown and always get the same value:
Foo.QueryInterface(IUnknown, I1);
I1.QueryInterface(IFooBar, B);
B.QueryInterface(IUnknown, I2);
Assert(I1 = I2);
If you just want to generate new objects of type TFooBar, then give your interface a method that generates those:
function TFoo.NewFooBar: IFooBar;
begin
Result := TFooBar.Create(Self) as IFooBar;
end;
Besides of Rob's remarks of breaking the rules here, you can even succeed with this construct:
function TFoo.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if IsEqualGUID(IID, IFooBar) then
Result := TFooBar.Create(Self).QueryInterface(IID, obj)
else
Result := inherited QueryInterface(IID, {out}Obj);
end;
I didn't investigate this, but you might get some problems with reference counting...
Based on the implementation of TObject.GetInterface in System.pas I would suggest this:
Pointer(Obj) := TFooBar.Create(Self);
Related
I have a class with multiple variables, which can be accessed by their own property:
TGame = class(TObject)
strict private
FValue1 : Integer;
FValue2 : Integer;
private
procedure SetValue1(const Value : Integer);
procedure SetValue2(const Value : Integer);
function GetValue1() : Integer;
function GetValue2() : Integer;
public
property Value1 : Integer read GetValue1 write SetValue1;
property Value2 : Integer read GetValue2 write SetValue2;
I am wondering, if there is a way to use the same getter and setter for different properties, like this:
property Value1 : Integer read GetValue write SetValue;
property Value2 : Integer read GetValue write SetValue;
Yes, this can be achieved using index specifiers:
Index specifiers allow several properties to share the same access method while representing different values.
For example,
type
TTest = class
strict private
FValues: array[0..1] of Integer;
function GetValue(Index: Integer): Integer;
procedure SetValue(Index: Integer; const Value: Integer);
public
property Value1: Integer index 0 read GetValue write SetValue;
property Value2: Integer index 1 read GetValue write SetValue;
end;
{ TTest }
function TTest.GetValue(Index: Integer): Integer;
begin
Result := FValues[Index];
end;
procedure TTest.SetValue(Index: Integer; const Value: Integer);
begin
FValues[Index] := Value;
end;
Of course, this also works with your original private fields:
type
TTest = class
strict private
FValue1: Integer;
FValue2: Integer;
function GetValue(Index: Integer): Integer;
procedure SetValue(Index: Integer; const Value: Integer);
public
property Value1: Integer index 1 read GetValue write SetValue;
property Value2: Integer index 2 read GetValue write SetValue;
end;
{ TTest }
function TTest.GetValue(Index: Integer): Integer;
begin
case Index of
1:
Result := FValue1;
2:
Result := FValue2;
else
raise Exception.Create('Invalid index.');
end;
end;
procedure TTest.SetValue(Index: Integer; const Value: Integer);
begin
case Index of
1:
FValue1 := Value;
2:
FValue2 := Value;
end;
end;
But it almost seems like you would rather need an array property:
type
TTest = class
strict private
FValues: array[0..1] of Integer;
function GetValue(Index: Integer): Integer;
procedure SetValue(Index: Integer; const Value: Integer);
public
property Values[Index: Integer]: Integer read GetValue write SetValue;
end;
{ TTest }
function TTest.GetValue(Index: Integer): Integer;
begin
Result := FValues[Index];
end;
procedure TTest.SetValue(Index: Integer; const Value: Integer);
begin
FValues[Index] := Value;
end;
We have a funny one.
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
ITestInterface = interface(IInvokable)
['{4059D1CD-A342-48EE-B796-84B8B5589AED}']
function GetPort: string;
function GetRoot: string;
end;
TTestInterface = class(TInterfacedObject, ITestInterface)
private
FPort: string;
FRoot: string;
public
constructor Create(FileName: TFileName);
destructor Destroy; override;
function GetPort: string;
function GetRoot: string;
end;
{ TTestInterface }
constructor TTestInterface.Create(FileName: TFileName);
begin
FPort := '8080';
FRoot := 'top';
end;
destructor TTestInterface.Destroy;
begin
// ^ Place Breakpoint here
inherited;
end;
function TTestInterface.GetPort: string;
begin
Result := FPort;
end;
function TTestInterface.GetRoot: string;
begin
Result := FRoot;
end;
type
TTestService = class
protected
FTest : TTestInterface;
public
constructor Create;
destructor Destroy; override;
procedure Process;
end;
{ TTestService }
constructor TTestService.Create;
begin
FTest := TTestInterface.Create('');
(FTest as IInterface)._AddRef;
end;
destructor TTestService.Destroy;
begin
FTest.Free;
inherited;
end;
procedure TTestService.Process;
begin
writeln( 'Config Root: ', FTest.GetRoot );
writeln( 'Config Port: ', FTest.GetPort );
end;
var
TS : TTestService;
begin
TS := TTestService.Create;
try
TS.Process;
finally
TS.Free;
end;
end.
When this application finishes it generates an Invalid Pointer Operation.
The really strange part is that setting a break point on the destructor, you can see that it generates the error the first time it gets called, which rules out it being freed twice. It is almost as if the object is dumped from memory without calling the destructor at all.
By removing the _AddRef everything works as expected.
We managed to produce this on Delphi 6. Can anyone confirm this behavior on any other version?
Use two variables: one for the class, and one for the interface.
Use the interface variable to manage the instance lifetime. Don't call free, but set the interface variable to nil (or out of scope) to let the instance running.
Use the class variable to have direct raw access to the instance, if needed - but it shouldn't be the case, or at least let the class be accessible only from protected/private members of the owner class.
So your code becomes:
type
TTestService = class
protected
FTest: ITestInterface;
FTestInstance : TTestInterface;
public
constructor Create;
procedure Process;
end;
{ TTestService }
constructor TTestService.Create;
begin
FTestInstance := TTestInterface.Create('');
FTest := FTestInstance;
end;
procedure TTestService.Process;
begin
writeln( 'Config Root: ', FTest.GetRoot );
writeln( 'Config Port: ', FTest.GetPort );
end;
var
TS : TTestService;
begin
TS := TTestService.Create;
try
TS.Process;
finally
TS.Free;
end;
end.
The problem is that you are manually freeing an interfaced object that has a reference count greater than zero. The exception is raised here :
procedure TInterfacedObject.BeforeDestruction;
begin
if RefCount <> 0 then {!! RefCount is still 1 - you made it that way!}
Error(reInvalidPtr);
end;
So... you could just call (FTest as IInterface)._Release; in the destructor in place of FTest.Free, but this feels like fixing one mistake by making another. Either you want reference counting or you don't - if you do, then you should work with the object in that way (using interface variables and letting scope and variable lifetime manage the object lifetime). If you don't want reference counting then disable it. Either way you should pick a lifetime management model and work with it in the normal way.
Case 1 : Disable Reference Counting
If you want to disable automatic reference counting and you're using Delphi 2009 or higher you can simply do this by inheriting from TSingletonImplementation instead of TInterfacedObject :
TTestInterface = class(TSingletonImplementation, ITestInterface)
private
FPort: string;
FRoot: string;
public
constructor Create(FileName: TFileName);
destructor Destroy; override;
function GetPort: string;
function GetRoot: string;
end;
Otherwise, you can implement this yourself by adding the required methods :
TTestInterface = class(TObject, ITestInterface)
private
FPort: string;
FRoot: string;
{ ** Add interface handling methods ** }
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
{ ** ---------------------- ** }
public
constructor Create(FileName: TFileName);
destructor Destroy; override;
function GetPort: string;
function GetRoot: string;
end;
which you implement as :
function TTestInterface.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := S_OK
else
Result := E_NOINTERFACE;
end;
function TTestInterface._AddRef: Integer;
begin
Result := -1;
end;
function TTestInterface._Release: Integer;
begin
Result := -1;
end;
Case 2 : Use Interface References Normally
If you absolutely need reference counting and you still need to access the concrete class members then the simplest solution is to strictly use interface variables, let your container class pin the object lifetime, and cast to the concrete type when needed. Lets introduce some state to the class :
TTestInterface = class(TInterfacedObject, ITestInterface)
private
FPort: string;
FRoot: string;
public
Foo : integer; { not an interface member...}
constructor Create(FileName: TFileName);
destructor Destroy; override;
function GetPort: string;
function GetRoot: string;
end;
Your container class then becomes :
type
TTestService = class
protected
FTest : ITestInterface;
public
constructor Create;
procedure Process;
end;
{ TTestService }
constructor TTestService.Create;
begin
FTest := TTestInterface.Create('');
end;
procedure TTestService.Process;
begin
writeln( 'Config Root: ', FTest.GetRoot );
writeln( 'Config Port: ', FTest.GetPort );
WriteLn( 'Foo : ', TTestInterface(FTest).Foo); {Cast to access class members}
end;
Note that the above cast of TTestInterface(FTest) only works in Delphi 2010 and higher. For versions older than this you must keep a separate object reference as in #ArnaudBouchez's answer. In either case, the point is to use interface references in the normal way to manage the object lifetime and to not rely on hacking the reference count manually.
I'm experimenting with Delphi 10 Seattle and trying to create my first Generic Container class. I need help with a Generic Comparer
Here a simple Hash object which I created:
type
TsmHeap<T> = class
private
fList: TList<T>;
Comparer: TComparer<T>;
procedure GetChildren(ParentIndex: integer; var Child1, Child2: integer);
function GetParent(ChildIndex: integer): integer;
function GetCapacity: integer;
function GetCount: integer;
function MustSwap(iParent, iChild: integer): boolean;
procedure SetCapacity(const Value: integer);
public
constructor Create(aComparer: TComparer<T>); overload;
constructor Create(aComparer: TCOmparer<T>; aCapacity: integer); overload;
destructor Destroy; override;
//-- Methods & Functions
function Dequeue: T;
procedure Enqueue(Item: T);
function IsEmpty: boolean;
//-- Properties
property Count: integer read GetCount;
property Capacity: integer read GetCapacity write SetCapacity;
end;
I've write the code for the methods and it compiles on its own with no problems. However when I try to create an integer version of the class I cannot get it to compile.
The problematic code is:
iHeap := TsmHeap<integer>.Create(TComparer<integer>.Construct(
function(const Left, Right: integer): integer
begin
result := Sign(Left - Right);
end)
);
This give a "E2250 There is no overloaded version of 'Create' that can be called with these arguments"
What am I doing wrong? How do I create the Comparer?
TComparer<T>.Construct returns IComparer<T> - it is a class function and not a constructor. Just change the parameter type of TsmHeap<T>.Create to IComparer<T> and it should work.
I need a base class like TInterfacedObject but without reference counting (so a kind of TNonRefCountedInterfacedObject).
This actually is the nth time I need such a class and somehow I always end up writing (read: copy and pasting) my own again and again. I cannot believe that there is no "official" base class I can use.
Is there a base class somewhere in the RTL implementing IInterface but without reference counting which I can derive my classes from?
In the unit Generics.Defaults there is a class TSingletonImplementation defined. Available in Delphi 2009 and above.
// A non-reference-counted IInterface implementation.
TSingletonImplementation = class(TObject, IInterface)
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
I did this. It can be used in place of TInterfacedObject with or without reference counting. It also has a name property - very useful when debugging.
// TArtInterfacedObject
// =============================================================================
// An object that supports interfaces, allowing naming and optional reference counting
type
TArtInterfacedObject = class( TInterfacedObject )
constructor Create( AReferenceCounted : boolean = True);
PRIVATE
FName : string;
FReferenceCounted : boolean;
PROTECTED
procedure SetName( const AName : string ); virtual;
PUBLIC
property Name : string
read FName
write SetName;
function QueryInterface(const AGUID : TGUID; out Obj): HResult; stdcall;
function SupportsInterface( const AGUID : TGUID ) : boolean;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
// =============================================================================
{ TArtInterfacedObject }
constructor TArtInterfacedObject.Create( AReferenceCounted : boolean = True);
begin
inherited Create;
FName := '';
FReferenceCounted := AReferenceCounted;
end;
function TArtInterfacedObject.QueryInterface(const AGUID: TGUID; out Obj): HResult;
const
E_NOINTERFACE = HResult($80004002);
begin
If FReferenceCounted then
Result := inherited QueryInterface( AGUID, Obj )
else
if GetInterface(AGUID, Obj) then Result := 0 else Result := E_NOINTERFACE;
end;
procedure TArtInterfacedObject.SetName(const AName: string);
begin
FName := AName;
end;
function TArtInterfacedObject.SupportsInterface(
const AGUID: TGUID): boolean;
var
P : TObject;
begin
Result := QueryInterface( AGUID, P ) = S_OK;
end;
function TArtInterfacedObject._AddRef: Integer;
begin
If FReferenceCounted then
Result := inherited _AddRef
else
Result := -1 // -1 indicates no reference counting is taking place
end;
function TArtInterfacedObject._Release: Integer;
begin
If FReferenceCounted then
Result := inherited _Release
else
Result := -1 // -1 indicates no reference counting is taking place
end;
// =============================================================================
You might consider TInterfacedPersistent. If you don't override GetOwner it does no ref-counting.
I don't know of any out-of-the-box base class, so I wrote my own (like you). Just put it in a common utils unit and you are done.
type
TPureInterfacedObject = class(TObject, IInterface)
protected
{ IInterface }
function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
{ TPureInterfacedObject }
function TPureInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
Result := E_NOINTERFACE;
end;
function TPureInterfacedObject._AddRef: Integer;
begin
Result := -1;
end;
function TPureInterfacedObject._Release: Integer;
begin
Result := -1;
end;
There is no such class, but you can easily write your own, as others have shown. I do, however, wonder why you would need it. In my experience, there is seldom a real need for such a class, even if you want to mix object and interface references.
Also note that when you use such a class, you'll still have to take care of setting any interface references you have to such an object to nil before they leave scope and before you free the object. Otherwise you might get the situation the runtime tries to call _Release on a freed object, and that tends to cause an invalid pointer exception.
IOW, I would advise against using such a class at all.
As of Delphi 11 Embarcadero added TNoRefCountObject to the System unit. Here's the note from the release notes:
The new class System.TNoRefCountObject is a non-reference-counted
IInterface implementation (replacing the old and oddly named
TSingletonObject)
I try to do list of procedures this way:
type
TProc = procedure of object;
TMyClass=class
private
fList:Tlist;
function getItem(index:integer):TProc;
{....}
public
{....}
end;
implementation
{....}
function TMyClass.getItem(index: Integer): TProc;
begin
Result:= TProc(flist[index]);// <--- error is here!
end;
{....}
end.
and get error:
E2089 Invalid typecast
How can I fix it?
As I see, I can make a fake class with only one property Proc:TProc; and make list of it. But I feel that it's a bad way, isn't it?
PS: project have to be delphi-7-compatible.
The typecast is invalid because you can not fit a method pointer to a pointer, a method pointer is in fact two pointers first being the address of the method and the second being a reference to the object that the method belongs. See Procedural Types in the documentation. This will not work in any version of Delphi.
Sertac has explained why your code doesn't work. In order to implement a list of such things in Delphi 7 you can do something like this.
type
PProc = ^TProc;
TProc = procedure of object;
TProcList = class(TList)
private
FList: TList;
function GetCount: Integer;
function GetItem(Index: Integer): TProc;
procedure SetItem(Index: Integer; const Item: TProc);
public
constructor Create;
destructor Destroy; override;
property Count: Integer read GetCount;
property Items[Index: Integer]: TProc read GetItem write SetItem; default;
function Add(const Item: TProc): Integer;
procedure Delete(Index: Integer);
procedure Clear;
end;
type
TProcListContainer = class(TList)
protected
procedure Notify(Ptr: Pointer; Action: TListNotification); override;
end;
procedure TProcListContainer.Notify(Ptr: Pointer; Action: TListNotification);
begin
inherited;
case Action of
lnDeleted:
Dispose(Ptr);
end;
end;
constructor TProcList.Create;
begin
inherited;
FList := TProcListContainer.Create;
end;
destructor TProcList.Destroy;
begin
FList.Free;
inherited;
end;
function TProcList.GetCount: Integer;
begin
Result := FList.Count;
end;
function TProcList.GetItem(Index: Integer): TProc;
begin
Result := PProc(FList[Index])^;
end;
procedure TProcList.SetItem(Index: Integer; const Item: TProc);
var
P: PProc;
begin
New(P);
P^ := Item;
FList[Index] := P;
end;
function TProcList.Add(const Item: TProc): Integer;
var
P: PProc;
begin
New(P);
P^ := Item;
Result := FList.Add(P);
end;
procedure TProcList.Delete(Index: Integer);
begin
FList.Delete(Index);
end;
procedure TProcList.Clear;
begin
FList.Clear;
end;
Disclaimer: completely untested code, use at your own risk.