How can I find the invalid class when C++/WinRT tells me that the class may not be final?

Raymond Chen

A customer was porting some C++/CX code to C++/WinRT and one of the things they did was change the nonstandard C++/CX sealed keyword to the standard final keyword. They were a bit too aggressive about applying the final keyword, though, because they were getting this compiler error:

Code Description File Line
C2338 C++/WinRT implementation types must not be final base.h 7759

The customer wanted to know which class was the one that was erroneously declared as final. Can C++/WinRT be improved so the error message includes the name of the bad type?

Okay, so second question first.

This error message is generated by a static_assert, and the rules for static_assert say that the message must be a string literal. It cannot be created dynamically. The error message is fixed; you can’t incorporate any compile-time information into it.

However, not all is lost.

If you look at the full error log, you can see that the compiler does tell you which class is the bad one:

base.h(7759,29): error C2338: C++/WinRT implementation types must not be final
contoso.cpp(556): message : see reference to function template instantiation 'auto winrt::make<winrt::Contoso::implementation::Widget>(void)' being compiled

This is a multi-line error message, but the Visual Studio error summary shows you only the first line. You have to flip to the Output window to see the second line, and that second line tells you where the problem is: The error message is coming from make<winrt::Contoso::implementation::Widget>, and that gives you a strong clue that the problem is with winrt::Contoso::implementation::Widget.

You can also get the information by reading the second error message:

Code Description File Line
C3246 ‘winrt::impl::heap_implements<D>’: cannot inherit from ‘winrt::Contoso::implementation::Widget‘ as it has been declared as ‘final’ base.h 7685

The full error text also tells you the identity of the bad base class:

base.h(7685,1): error C3246: 'winrt::impl::heap_implements<D>': cannot inherit from 'winrt::Contoso::implementation::Widget' as it has been declared as 'final'
with
[
    D=Contoso::implementation::Widget
]

3 comments

Discussion is closed. Login to edit/delete existing comments.

  • 紅樓鍮 0

    Though aren’t you going to talk about why a C++/WinRT implementation class can’t be final, and what winrt::impl::heap_implements is and what its purpose is? I don’t think it’ll be easy for the programmer to find the answer without reading that specific blog post by Kenny Kerr.

    • ஜெய்கணேஷ் குமரன் 0

      This was to disallow putting the WinRT implementation type on the stack or inside a C++ smart pointer type that’s incompatible with the COM reference counting model.

      In C++/WinRT version 1, all these would compile and were a significant source of bugs. Even the code generated by the XAML compiler did this mistake.

      struct T : winrt::implements {};
      
      T t;
      std::unique_ptr t = std::make_unique();
      std::shared_ptr t = std::make_shared();
      

      To prevent instantiating the type directly like this and ensure you go through winrt::make or winrt::make_self, in C++/WinRT version 2, they made the base class abstract with a pure virtual function named “use_make_function_to_create_this_object”. You as the type author don’t know that this exists unless you read the source and won’t naturally implement the function. This prevents you from instantiating the type directly.

      The struct template “winrt::impl::heap_implements” derives from your type and overrides the use_make_function_to_create_this_object, providing a dummy implementation so it is no longer abstract and can be instantiated. Basically winrt::make and winrt::make_self now “new up” heap_implements and wrap it in an appropriate smart pointer type (eg: com_ptr or a WinRT projected type).

      Of course, having this pure virtual function has an overhead, so it is disabled in release mode. You can also disable it in debug mode by defining WINRT_NO_MAKE_DETECTION.

  • Jeremy Richards 0

    One of my biggest frustrations when onboarding junior developers is convincing them that they need to keep the output window open and not just rely on the error window because it lacks context and sometimes has incomplete messages.

Feedback usabilla icon