09 July, 2016

Solve the "no GUID has been associated with this object" compiler error for __uuidof in templates instantinations

As you know WinRT has C++ interfaces ABI::Windows::Foundation::Collections::IIterable and ABI::Windows::Foundation::Collections::IIterable to make iteration in collections universal. This is the very good idea, but you got interfaces with templates: (see windows.foundation.collections.h):

namespace ABI {
namespace Windows {
namespace Foundation {
namespace Collections { 

    template <class T> 
    struct IIterator 
        : IIterator_impl<T>
        , detail::not_yet_specialized<IIterator<T>>
    {
    };

    template <class T> 
    struct IIterable
        : IIterable_impl<T>
        , detail::not_yet_specialized<IIterable<T>>
    {
    };

    template <class T, bool isStruct>
    struct IIterator_impl : IInspectable
    {
    private:
        typedef typename Windows::Foundation::Internal::GetAbiType<T>::type     T_abi;
        typedef typename Windows::Foundation::Internal::GetLogicalType<T>::type T_logical;
    public:
        typedef T                                                               T_complex;

        virtual /* propget */ HRESULT STDMETHODCALLTYPE get_Current(_Out_ T_abi *current) = 0;
        virtual /* propget */ HRESULT STDMETHODCALLTYPE get_HasCurrent(_Out_ boolean *hasCurrent) = 0;
        virtual HRESULT STDMETHODCALLTYPE MoveNext(_Out_ boolean *hasCurrent) = 0;
        virtual HRESULT STDMETHODCALLTYPE GetMany(_In_ unsigned capacity, _Out_writes_to_(capacity,*actual) T_abi *value, _Out_ unsigned *actual) = 0;
    };

    template <class T>
    struct IIterable_impl : IInspectable
    {
    private:
        typedef typename Windows::Foundation::Internal::GetAbiType<T>::type     T_abi;
        typedef typename Windows::Foundation::Internal::GetLogicalType<T>::type T_logical;
    public:
         typedef T                                                               T_complex;

        virtual HRESULT STDMETHODCALLTYPE First(_Outptr_result_maybenull_ IIterator<T_logical> **first) = 0;
    };

}}}}

There are also a lot instantinations of interfaces. Here ones for ABI::Windows::ApplicationModel::Package (see Windows.ApplicationModel.h):

namespace ABI {
namespace Windows {
namespace Foundation {
namespace Collections {

template <>
struct __declspec(uuid("0217f069-025c-5ee6-a87f-e782e3b623ae"))
IIterator<ABI::Windows::ApplicationModel::Package*> : IIterator_impl<ABI::Windows::Foundation::Internal::AggregateType<ABI::Windows::ApplicationModel::Package*, ABI::Windows::ApplicationModel::IPackage*>> {
static const wchar_t* z_get_rc_name_impl() { return L"Windows.Foundation.Collections.IIterator`1<Windows.ApplicationModel.Package>"; }
};

template <>
struct __declspec(uuid("69ad6aa7-0c49-5f27-a5eb-ef4d59467b6d"))
IIterable<ABI::Windows::ApplicationModel::Package*> : IIterable_impl<ABI::Windows::Foundation::Internal::AggregateType<ABI::Windows::ApplicationModel::Package*, ABI::Windows::ApplicationModel::IPackage*>> {
static const wchar_t* z_get_rc_name_impl() { return L"Windows.Foundation.Collections.IIterable`1<Windows.ApplicationModel.Package>"; }
};

}}}}

I our project we are using own COM interface holder class which is similar _com_ptr_t, but with much conversion restrictions. Bellow some declarations for that:

#define _COM_SMARTPTR_TYPEDEF_EX(Interface, IID, Name) typedef _COM_SMARTPTR<_COM_SMARTPTR_LEVEL2<Interface, &IID>> Name##Ptr

namespace ABI {
namespace Windows {

namespace Foundation {
namespace Collections {
_COM_SMARTPTR_TYPEDEF_EX(IIterable<ABI::Windows::ApplicationModel::Package *>, __uuidof(IIterable<ABI::Windows::ApplicationModel::Package *>), IIterable_ABI_Windows_ApplicationModel_Package);
_COM_SMARTPTR_TYPEDEF_EX(IIterator<ABI::Windows::ApplicationModel::Package *>, __uuidof(IIterator<ABI::Windows::ApplicationModel::Package *>), IIterator_ABI_Windows_ApplicationModel_Package);
}}

namespace ApplicationModel {
_COM_SMARTPTR_TYPEDEF(IPackage, __uuidof(IPackage));
}

}}

It would be great to write template class to get ABI::Windows::Foundation::Collections::IIterable from ABI::Windows::Foundation::Collections::IIterable for specific type. Bellow the first variant which looks good, but unexpectable has compilation error:

template<
 typename _Iterable_t>
class winrt_iterator_from_iterable final
{
 using iface_type = ABI::Windows::Foundation::Collections::IIterator<typename ABI::Windows::Foundation::Internal::GetLogicalType<typename _Iterable_t::iface_type::T_complex>::type>;

public:
 typedef _COM_SMARTPTR<_COM_SMARTPTR_LEVEL2<iface_type, &__uuidof(iface_type)>> type;
};
error C2787: 'ABI::Windows::Foundation::Collections::IIterator<ABI::Windows::Foundation::Internal::GetLogicalType<_Iterable_t::iface_type::T_complex>::type>' : no GUID has been associated with this object

May I'm wrong, but it looks like as compiler error. When you read the error message your see that compiler doesn't make type inference for __uuidof. Well, let's help the compiler:

template<
 typename _Iterable_t>
class winrt_iterator_from_iterable final
{
 using iface_type = ABI::Windows::Foundation::Collections::IIterator<typename ABI::Windows::Foundation::Internal::GetLogicalType<typename _Iterable_t::iface_type::T_complex>::type>;

 static iface_type * const template_type_resolver() throw() { return nullptr; }

public:
 typedef _COM_SMARTPTR<_COM_SMARTPTR_LEVEL2<iface_type, &__uuidof(std::remove_pointer_t<decltype(template_type_resolver())>)>> type;
};

template_type_resolver() and decltype(template_type_resolver()) here make type inference! Here you are...

No comments: