Implementing Interfaces: Delphi and C++
Go Up to C++ Specifics Index
This topic describes the two methods of implementing interfaces in C++ and compares the Delphi method of implementing interfaces. Note that in C++, you can use two different strategies for implementing interfaces:
- The new
__property implements
attribute - Inheritance
Contents
Using __property implements
The __property implements
attribute enables C++ to more easily implement interfaces in a way that is analogous to the use of delegation by Delphi. For more information, see __property implements Support in C++Builder.
You do not have to reimplement and forward the methods of IUnknown when you implement interfaces in C++. Instead of deriving from TInterfacedObject, use the TCppInterfacedObject template: it handles IUnknown, and you handle the method specific to your interface.
Example:
class TMyPersist: public TCppInterfacedObject<IPersist>
{
HRESULT __stdcall GetClassID(CLSID *pClassID)
{
*pClassID = CLSID_SOMEVALUE;
return S_OK;
}
};
Comparing Delphi and C++ Implementation of Interfaces
Here are two examples of implements: One in Delphi and one in C++. The two examples expose the same interface, but the Delphi example is delay-initialized while the C++ one is not.
Delphi Example
unit Unit1;
interface
type
// Interface that exposes an 'Add' method
IAdder = interface
['{D0C74612-9E4D-459A-9304-FACE27E3577D}']
function Add(I, J: Integer): Integer;
end;
// Aggregatee that implements IAdder
TAdder = class(TAggregatedObject, IAdder)
function Add(I, J: Integer): Integer;
end;
// Aggregator - implements IAdder via TAdder
TPasClass = class(TInterfacedObject, IAdder)
FAdder: TAdder;
function GetAdder: TAdder;
public
destructor Destroy; override;
property Adder: TAdder read GetAdder write FAdder implements IAdder;
end;
function TestAdd(I, J: Integer): Integer;
implementation
{ TAdder }
function TAdder.Add(I, J: Integer): Integer;
begin
Result := I+J;
end;
{ TPasClass }
destructor TPasClass.Destroy;
begin
FAdder.Free;
inherited;
end;
function TPasClass.GetAdder: TAdder;
begin
if FAdder = nil then
FAdder := TAdder.Create(Self as IInterface);
Result := FAdder;
end;
// Adds using TClass' IAdder
function TestAdd(I, J: Integer): Integer;
var
punk: IInterface;
begin
punk := TPasClass.Create as IInterface;
Result := (punk as IAdder).Add(I, J);
end;
end.
C++ Example
The C++ example is not delay-initialized:
#include <stdio.h>
// Interface that exposes an Add(..) method
__interface INTERFACE_UUID("{D0C74612-9E4D-459A-9304-FACE27E3577D}") IAdder : public System::IInterface
{
virtual int __fastcall Add(int I, int J) = 0 ;
};
typedef System::DelphiInterface<IAdder> _di_IAdder;
// Aggregatee that implements IAdder
class TCppAdder: public TCppAggregatedObject<IAdder> {
public:
__fastcall TCppAdder(const _di_IInterface Controller) : _AGGREGATED_CLASS(Controller)
{}
int __fastcall Add(int i, int j)
{
return i+j;
}
};
// Aggregator - exposes IAdder via TCppAdder
class TCppClass : public TInterfacedObject {
private:
TCppAdder* obj;
IAdder* FAdder;
protected:
void initAggregatee()
{
_di_IInterface punk;
GetInterface(punk);
_di_IAdder adder;
(obj = new TCppAdder(punk))->GetInterface(adder);
FAdder = adder;
}
public:
__fastcall TCppClass()
{
initAggregatee();
}
__fastcall ~TCppClass()
{
delete obj;
}
__property IAdder* Adder = { read=FAdder, implements };
};
// Test: Invoke IAdder.Add(..) using TCppClass
int main()
{
TCppClass* ptr;
_di_IAdder ia;
if (!((ptr = new TCppClass())->GetInterface(ia)))
{
delete ptr;
}
else
{
int result = ia->Add(10, 20);
printf("Add Result = %d\n", result);
}
return 0;
}
Using Inheritance
The following example compares Delphi and C++ (using inheritance) for implementing an interface.
Here is the Delphi code to write a class that implements interface IPersist to define just a single method, GetClassID:
Delphi | |
---|---|
Interface |
IPersist = interface(IUnknown)
['0000010C-0000-0000-C000-000000000046']
function GetClassID(out classID: TCLSID): HResult; stdcall;
end;
|
Class |
TMyPersist = class(TInterfacedObject, IPersist)
function GetClassID(out classID: TCLSID): HResult; stdcall;
end;
function TMyPersist.GetClassID(out classID: TCLSID): HResult;
begin
classID := CLSID_SOMEVALUE;
Result := S_OK;
end;
|
As a comparison, here is the C++ code to write the same class when using inheritance:
C++ | |
---|---|
Interface |
struct __declspec(uuid("0000010c-0000-0000-C000-000000000046"))
IPersist : public IUnknown
{
public:
virtual HRESULT __stdcall GetClassID(CLSID *pClassID) = 0;
};
|
Class |
class TMyPersist: public TInterfacedObject, IPersist
{
HRESULT __stdcall GetClassID(CLSID *pClassID)
{
*pClassID = CLSID_SOMEVALUE;
return S_OK;
}
// Methods of IUnknown have to be reimplemented and forwarded :(
HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)
{ return GetInterface(IID, Obj) ?
S_OK : E_NOINTERFACE; }
ULONG __stdcall AddRef() { return TInterfacedObject::_AddRef(); }
ULONG __stdcall Release() { return TInterfacedObject::_Release(); }
};
|
ActiveX Helpers
A typical ActiveX control implements at least 21 interfaces, so implementing interfaces in C++Builder is quite tedious unless you use the __property implements
attribute.
The Delphi ActiveX framework (DAX) provides helper classes (such as TConnectionPoints and TPropertyPageImpl) whose parent classes (in this case, TActiveXControl and TActiveXPropertyPage) are not directly derived from the interfaces implemented by the helpers. Delphi uses the implements
directive in property declarations that map to the helpers. It is the implements directive that causes the Delphi compiler record the interface in the RTTI InterfaceTable of the class.
Similarly, the __property implements
attribute causes the C++ compiler to record the interface in the RTTI InterfaceTable of the class. This table is how IUnknown.QueryInterface is implemented, and therefore this table tells the outside world that a particular class implements an interface.
In the C++ TMyPersist example above, you can see that QueryInterface simply calls TObject.GetInterface:
// Methods of IUnknown have to be reimplemented and forwarded :(
HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)
{ return GetInterface(IID, Obj) ?
S_OK : E_NOINTERFACE; }
TObject.GetInterface is driven off the InterfaceTable in a class's metadata. If there is no InterfaceTable entry for an interface, then the class must write a raw implementation of QueryInterface to handle each interface it implements rather than let the existing logic in core TObject handle this chore. So this is the reason why COM developers typically use frameworks such as ATL, MFC, BaseCtl, and DAX instead of writing raw IUnknown implementations.
C++ would not be able to take advantage of ActiveX helpers provided by DAX without some support for __property implements
.