not quite minimalistic enough  

2019-06-29

Saving some typing.

One item on my C++ wishlist is a syntax for inlining typedefs into the base class list on a class declaration:

class CMyClass
    : public ATL::CComObjectRootEx<ATL::CComSingleThreadModel>
    , public ATL::CComCoClass<CMyClass, &__uuidof(MyClass)>
    , public ATL::IConnectionPointContainerImpl<CMyClass>
    , public ATL::IConnectionPointImpl<CMyClass,
                 &__uuidof(IMyClassEvents)>
    , public ATL::IConnectionPointImpl<CMyClass,
                 &__uuidof(IMyClassOtherEvents)>
    , public IMyClass

The two connection points result in a duplicated name, m_vec. To get the list of listeners to deliver any given event to, contortions like ATL::IConnectionPointImpl<CMyClass, &__uuidof(IMyClassOtherEvents)>::m_vec are necessary. These usually become typedefs:

private:
    typedef ATL::IConnectionPointImpl<CMyClass, &__uuidof(IMyClassEvents)> events_cp;
    typedef ATL::IConnectionPointImpl<CMyClass, &__uuidof(IMyClassOtherEvents)> otherevents_cp;

Then a relatively simple otherevents_cp::m_vec leads to the right container, but it requires regurgitating the entire base class type in the typedef.

Something like this would be really nice to have instead:

class CMyClass
    : public ATL::CComObjectRootEx<ATL::CComSingleThreadModel>
    , public ATL::CComCoClass<CMyClass, &__uuidof(MyClass)>
    , public ATL::IConnectionPointContainerImpl<CMyClass>
    , public ATL::IConnectionPointImpl<CMyClass,
                 &__uuidof(IMyClassEvents)> [private: events_cp]
    , public ATL::IConnectionPointImpl<CMyClass,
                 &__uuidof(IMyClassOtherEvents)> [private: otherevents_cp]
    , public IMyClass

Wouldn’t it?

Why stick that there?

I know it’s because the class is from waaay back when the SBCS APIs were still actually being used, but …

// Extract a const wchar_t*
//
inline _bstr_t::operator const wchar_t*() const throw()
{
    return (m_Data != NULL) ? m_Data->GetWString() : NULL;
}

// Extract a const char_t*
//
inline _bstr_t::operator const char*() const
{
    return (m_Data != NULL) ? m_Data->GetString() : NULL;
}

BSTR is a Unicode string, and the second extractor above converts it to ANSI. This, unfortunately, results in ambiguous-overload errors whenever a _bstr_t is passed to anything that can accept both forms.

There should really be a way to, say, #define _BSTR_T_NO_ANSI_SUPPORT.

2019-06-28

Library Science!

Huh? I thought type libraries could only represent the Automation compatible types?

Good to know. #import is much easier than writing all the wrappers myself, particularly because I’ve been rather generous with the property attributes this time.

Templates. It's always the templates that get you.

Problem

You are developing a COM object that supports outgoing events (source interfaces). You get a compiler error similar to

C2440: ‘static_cast’ cannot convert from ‘CMyClass::_atl_conn_classtype *’ to ‘ATL::_ICPLocator<IID_IMyClassEvents> *'. Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast.

on a CONNECTION_POINT_ENTRY() line:

class CMyClass
   : public ATL::CComObjectRootEx<ATL::CComSingleThreadModel>
   , public ATL::CComCoClass<CMyClass, &CLSID_MyClass>
   , public ATL::IConnectionPointContainerImpl<CMyClass>
   , public ATL::IConnectionPointImpl<CMyClass, &__uuidof(IMyClassEvents)>
   , public IMyClass
{
public:
   CMyClass();

   DECLARE_PROTECT_FINAL_CONSTRUCT()

   BEGIN_COM_MAP(CMyClass)
      COM_INTERFACE_ENTRY(IMyClass)
      COM_INTERFACE_ENTRY(IConnectionPointContainer)
   END_COM_MAP()

   BEGIN_CONNECTION_POINT_MAP(CMyClass)
      CONNECTION_POINT_ENTRY(IID_IMyClassEvents)    // <-- C2440
   END_CONNECTION_POINT_MAP()
...

Analysis

The IIDs in the IConnectionPointImpl base class and the CONNECTION_POINT_ENTRY are not the same symbol. The cast fails because it attempts to convert to a base-class pointer that is not a base class, but a separate specialization of the same template.

Solution

Use either the predefined constants (IID_IMyClassEvents) or the MIDL metadata based constants (__uuidof(IMyClassEvents)) in both places; do not mix them.