2
votes

The accepted answer in this question shows how to check for and use an interface on a VCL-derived object in Delphi.

How to use Interface with VCL Classes - Part 2

procedure Test;
var
  O1: TSomeThing;
  Intf: ISomething;
begin
  O1 := TSomeThing.Create(nil);

  if Supports(O1, ISomething, Intf) then
  begin
    Intf.DoSomething;
  end;

Basically, I want to do the same in C++Builder but haven't worked out how to use "Supports" from C++.

Attempting to use <dynamic_cast> fails at compile time when using VCL-derived classes...

TSomeThing * O1;
ISomething *i = dynamic_cast<ISomething*>(O1);  // Error: Can't cast

The suggested Inheritance and Interfaces article mentions TObject::GetInterface() but when I try that I get an error "Pure Virtual Function called".

_di_IFoo mc;

if (this->GetInterface(mc)) ... 

Update: First, the objects I'm adding interfaces to are existing VCL controls so NOT derived from TInterfacedObject.

Second - there's no COM involved - I hope! The use of Interfaces is purely to allow me to use the concept of interfaces for multiple inheritance with VCL components which C++Builder (at least, in 2010) does not support.

So my interface looks like this (NOTE that there's no __addref/__release etc...):

 __interface INTERFACE_UUID("{C527B88F-3F8E-1134-80e0-01A04F57B270}") IMyInterface : public IInterface
 {
 public:
     virtual UTF8String getHello() = 0;
 };

And my objects looks like this

class TMyPanel: public TPanel, IMyInterface
{
    ...
public:
    UTF8String getHello() { return "Hello from a TMyPanel";}
    ...
};

class TMyLabel: public TLabel, IMyInterface
{
    ...
public:
    UTF8String getHello() { return "Hello from a TMyLabel";}
    ...
};

That's straightforward, and as described in the Embarcadero docs for Interfaces.

But, how to tell if a particular TObject supports IMyInterface???

The following template function does this for me, based on TObject.GetInterfaceEntry() from system.pas:

template<typename T>
T* getInterface(TObject *obj)
{
    T *intf = NULL;

    PInterfaceEntry interfaceEntry = obj->GetInterfaceEntry(__uuidof(T) );
    if (interfaceEntry && interfaceEntry->IOffset != 0)
    {
        intf = (T*)(((char *)obj) + interfaceEntry->IOffset);
    }

    return intf;
}

And we use it like this:

IMyInterface *myIf = getInterface<IMyInterface>(aRandomTObject);
if (myIf)
{
    UTF8String s = myIf->getHello();
}

Please let me know if there's a better way than this, as the VTable/Pointer spelunking makes my teeth itch...

1
@J... Maybe I'm being dumb and need spoon-feeding, but I'd read that page before and couldn't find an example showing how to determine if an Object supports a particular Interface.Roddy
@J... No, the object type isn't known at compile-time, and - because I'm adding interfaces to VCL control they are not derived from TInterfacedObject, so no Supports(). I have an 'answer' which works for me - but feels scary. I'll post it and see who chokes on their beer.Roddy
You don't need TInterfacedObject to use Supports(). It can query a TObject for its interface table and then enumerate it looking for the requested interface.Remy Lebeau
@RemyLebeau - I see - I can use ::Supports() but not this->Supports() ??Roddy

1 Answers

4
votes

You do it the same way in C++ as in Delphi - via the Sysutils::Supports() function.

This works for me when I try it:

//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    void __fastcall FormClick(TObject *Sender);
private:    // User declarations
    TPanel *p;
    TLabel *l;
    void Test();
public:     // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm7 *Form1;
//---------------------------------------------------------------------------
__interface INTERFACE_UUID("{C527B88F-3F8E-1134-80e0-01A04F57B270}") IMyInterface : public IInterface
{
public:
    virtual UTF8String getHello() = 0;
};

#if !defined(INTFOBJECT_IMPL_IUNKNOWN)
#define INTFOBJECT_IMPL_IUNKNOWN(BASE) \
    ULONG   __stdcall AddRef() { return BASE::_AddRef();} \
    ULONG   __stdcall Release(){ return BASE::_Release();} \
    HRESULT __stdcall QueryInterface(REFIID iid, void** p){ return BASE::QueryInterface(iid, p);} 
#endif

class TMyPanel : public TPanel, public IMyInterface
{
    INTFOBJECT_IMPL_IUNKNOWN(TPanel)
public:
    __fastcall TMyPanel(TComponent *Owner) : TPanel(Owner) {}
    UTF8String getHello() { return "Hello from a TMyPanel"; }
};

class TMyLabel : public TLabel, public IMyInterface
{
    INTFOBJECT_IMPL_IUNKNOWN(TLabel)
public:
    __fastcall TMyLabel(TComponent *Owner) : TLabel(Owner) {}
    UTF8String getHello() { return "Hello from a TMyLabel"; }
};
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    p = new TMyPanel(this);
    p->Parent = this;

    l = new TMyLabel(this);
    l->Parent = p;
    l->Caption = L"Test";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClick(TObject *Sender)
{
    Test();
}
//---------------------------------------------------------------------------
void TForm1::Test()
{
    DelphiInterface<IMyInterface> Intf;

    if (Supports(p, __uuidof(IMyInterface), (void*)&Intf))
    {
        UTF8String s = Intf->getHello();
        ShowMessage(s);
        Intf.Release();
}

    if (Supports(l, __uuidof(IMyInterface), (void*)&Intf))
    {
        UTF8String s = Intf->getHello();
        ShowMessage(s);
        Intf.Release();
    }
}
//---------------------------------------------------------------------------