Last Modification: December 12, 2001

HOWTO: Reuse interface implementation in source in ATL projects - part II: base and derived interfaces

A common phenomenon during the evolution of a component is the development of newer extended version of one or more interfaces usually by deriving from the old interfaces:


interface ISomething : IUnknown
interface ISomething2 : ISomething
interface ISomething3 : ISomething2

Another possible case is designing the component to have it implement a series of cascaded interfaces - interfaces that derive in a chain:


interface IBase : IUnknown
interface IDerived : IBase
interface IMoreDerived : IDerived
interface IAnotherDerived : IBase
interface IAnotherMoreDerived : IAnotherDerived

In both cases it is desirable to share the implementation of the base interfaces throughout the project. This is accomplished through the use of C++ templates. Let's consider the following simple derivation chain:


interface IBase : IUnknown
interface IDerived : IBase

The C++ templates we'll use look as following:


template <typename Itf>
class IBaseImpl : public Itf
{
    // Implement the IBase methods here
};

template <typename Itf>
class IDerivedImpl : public Itf
{
    // Implement the IDerived methods here
};

And a class that's using these implementations would look like the following:


class ATL_NO_VTABLE CAtlObj :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CAtlObj, &CLSID_AtlObj>,
    public IDerivedImpl< IBaseImpl<IDerived> >
{
public:
    CAtlObj() {}

DECLARE_REGISTRY_RESOURCEID(IDR_ATLOBJ)

DECLARE_NOT_AGGREGATABLE(CAtlObj)

BEGIN_COM_MAP(CAtlObj)
    COM_INTERFACE_ENTRY(IBase)
    COM_INTERFACE_ENTRY(IDerived)
END_COM_MAP()
};

Note IDerivedImpl<> does not derive from IBaseImpl<>, but instead it is passed in as a template parameter. This allows you to have multiple implementations of IBase and use them wherever appropriate. However, if this is not essential for your design, but you do have a deep interface inheritance hierarchy, it might be beneficial to simplify the ATL object class' derivation by explicitly deriving from the implementation of the base interface in the implementation of the derived interface:


template <typename Itf>
class IBaseImpl : public Itf
{
    // Implement the IBase methods here
};

template <typename Itf>
class IDerivedImpl : public IBaseImpl<Itf>
{
    // Implement the IDerived methods here
};

class ATL_NO_VTABLE CAtlObj :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CAtlObj, &CLSID_AtlObj>,
    public IDerivedImpl<IDerived>
{
public:
    CAtlObj() {}

DECLARE_REGISTRY_RESOURCEID(IDR_ATLOBJ)

DECLARE_NOT_AGGREGATABLE(CAtlObj)

BEGIN_COM_MAP(CAtlObj)
    COM_INTERFACE_ENTRY(IBase)
    COM_INTERFACE_ENTRY(IDerived)
END_COM_MAP()
};

Please read the first part of this article for techniques on how to deal with code bloat and dual interfaces. Note you can mix the techniques from this and the first part:


template <typename T, typename Itf>
class IDerivedImpl : public Itf
{
    // Implement the IDerived methods here
    // In a method that requires services from the derived class
    {
        T* pT = static_cast<T*>(this);
        pT->SomeMethod();
        ...
    }
};

class ATL_NO_VTABLE CAtlObj :
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CAtlObj, &CLSID_AtlObj>,
    public IDerivedImpl< CAtlObj, IBaseImpl<IDerived> >
{
    ...
};

References and samples: