Implementing Interfaces: Delphi and C++

From RAD Studio
Jump to: navigation, search

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

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.

See Also