April 17, 1998

Handling Name Collision Using Forwarding Shims

One of the problems with MI is that of name collisions. Imagine the following interfaces:

interface ICowboy : IUnknown {
    HRESULT Draw();
};

interface IArtist : IUnknown {
    HRESULT Draw();
};

Because both Draw methods have the same signature, using straight MI requires a single shared implementation:

// Ace Powell was a cowboy/artist who lived in the
// western US from 1912 to his death in 1978. I’d
// like to thank Tim Ewald for this fabulous example,
// which I have used to death for years.

class CAcePowell :
    public CComObjectRootEx,
    public ICowboy,
    public IArtist {

public:

BEGIN_COM_MAP(CAcePowell)
    COM_INTERFACE_ENTRY(ICowboy)
    COM_INTERFACE_ENTRY(IArtist)
    END_COM_MAP()

    HRESULT Draw()
    { /* Act as a cowboy or an artist? */ }

};

Since the implied meaning of Draw is very different for an artist than it is for a cowboy, we’d like to be able to provide two Draw implementations. For that, we a technique long known to the C++ community that I’ll call forwarding shims.”

The problem is that C++ has no syntax to be able to distinguish methods with the same signature from different bases in the derived class. For example, the following is not legal C++:

class CAcePowell :
    public CComObjectRootEx,
    public ICowboy,
    public IArtist {

public:

BEGIN_COM_MAP(CAcePowell)
    COM_INTERFACE_ENTRY(ICowboy)
    COM_INTERFACE_ENTRY(IArtist)
END_COM_MAP()

    HRESULT IArtist::Draw(); // error
    HRESULT ICowboy::Draw(); // error

};

However, we can certainly distinguish the methods in individual base classes, e.g.

struct _IArtist : public IArtist {
    STDMETHODIMP Draw() { return ArtistDraw(); }
    STDMETHOD(ArtistDraw)() =0;
};

struct _ICowboy : public ICowboy {
    STDMETHODIMP Draw() { return CowboyDraw(); }
    STDMETHOD(CowboyDraw)() =0;
};

Both _IArtist and _ICowboy are shim classes that implement the method with the conflicting name and forward to another pure virtual member function with a unique name. Since both shims derive from the interface in question, they interfaces IArtist and ICowboy can still appear in the interface map without difficulty:

class CAcePowell :
    public CComObjectRootEx,
    public _ICowboy,
    public _IArtist {

public:

BEGIN_COM_MAP(CAcePowell)
   COM_INTERFACE_ENTRY(ICowboy)
    COM_INTERFACE_ENTRY(IArtist)

END_COM_MAP()

    HRESULT ArtistDraw();
    HRESULT CowboyDraw();

};

This trick fills the vtables for IArtist and ICowboy with _IArtist::Draw and _ICowboy::Draw. These functions, in turn, forward to the more derived class’s implementation of the ArtistDraw and CowboyDraw. The forwarding shims remove our name conflict at the cost of an extra vtable per shim class, an extra entry per method per vtable and an extra virtual function invocation per call. If this extra cost bothers you, remove it using the standard ATL tricks:

template
struct ATL_NO_VTABLE _IArtist : public IArtist {
  STDMETHODIMP Draw() {
    return
      static_cast(this)->ArtistDraw();
  }
};

template
struct ATL_NO_VTABLE _ICowboy : public ICowboy {
  STDMETHODIMP Draw() {
    return
      static_cast(this)->CowboyDraw();
  }
};

class ATL_NO_VTABLE CAcePowell :
    public CComObjectRootEx,
    public _ICowboy,
    public _IArtist {

public:

BEGIN_COM_MAP(CAcePowell)
    COM_INTERFACE_ENTRY(ICowboy)
    COM_INTERFACE_ENTRY(IArtist)
END_COM_MAP()

    HRESULT ArtistDraw();
    HRESULT CowboyDraw();

};

Credit

Tim Ewald showed me this trick years ago. Jim Springfield showed me how it could be used with ATL. Don Box recommended the ATL_NO_VTABLE optimization.

Copyright

This web page is adapted from the book ATL Internals by Brent Rector and Chris Sells. Copyright � 1998 by Addison Wesley Longman, Inc. All rights reserved.