{"id":108800,"date":"2023-09-20T07:00:00","date_gmt":"2023-09-20T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108800"},"modified":"2023-09-20T07:01:14","modified_gmt":"2023-09-20T14:01:14","slug":"20230920-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230920-00\/?p=108800","title":{"rendered":"Why does my C++\/WinRT project get unresolved externals for constructors?"},"content":{"rendered":"<p>A customer was trying to build a C++\/WinRT project, and they got this linker error:<\/p>\n<pre style=\"white-space: pre-wrap;\">LNK2019 unresolved external symbol \"public: __cdecl winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>EventHandler&lt;<wbr \/>struct winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IInspectable&gt;::<wbr \/>EventHandler&lt;<wbr \/>struct winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IInspectable&gt;<wbr \/>&lt;class &lt;lambda_<wbr \/>5bea4cc895695129<wbr \/>4713c74881f70682&gt; &gt;<wbr \/>(class &lt;lambda_<wbr \/>5bea4cc895695129<wbr \/>4713c74881f70682&gt;)\"\r\n<\/pre>\n<p>Okay, the first order of business is trying to parse this thing.<\/p>\n<pre style=\"font-size: 80%;\">winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>EventHandler&lt;<wbr \/>struct winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IInspectable&gt;::\r\n                            EventHandler&lt;<wbr \/>struct winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IInspectable&gt;\r\n    &lt;class &lt;lambda_<wbr \/>5bea4cc895695129<wbr \/>4713c74881f70682&gt; &gt;\r\n    (class &lt;lambda_<wbr \/>5bea4cc895695129<wbr \/>4713c74881f70682&gt;)\r\n<\/pre>\n<p>After breaking it down this way, we see that this is the <code>EventHandler&lt;<wbr \/>struct winrt::<wbr \/>Windows::<wbr \/>Foundation::<wbr \/>IInspectable&gt;<\/code> constructor, specifically a templated constructor whose template parameters are <code>&lt;class &lt;lambda_<wbr \/>5bea4cc895695129<wbr \/>4713c74881f70682&gt; &gt;<\/code>.<\/p>\n<p>Okay, so the missing external is an <code>EventHandler<\/code> constructor.<\/p>\n<p>Some time ago, we investigated <a title=\"Why does my C++\/WinRT project get errors of the form &quot;unresolved external symbol ... consume_Something&quot;?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190530-00\/?p=102529\"> why a C++\/WinRT project gets errors of the form &#8220;unresolved external symbol \u2026 consume_Something&#8221;<\/a> and concluded that it was due to failure to include the header file for the namespace that being used. We then learned that the C++\/WinRT library did some error message metaprogramming so that the error is caught at compile time rather than link time. The trick is to have the method return <code>auto<\/code>: If you try to call an <code>auto<\/code>-returning function before it is defined, then the compiler complains that it doesn&#8217;t yet know what the function returns.<\/p>\n<p>Unfortunately, constructors don&#8217;t &#8220;return&#8221; anything, so the <code>auto<\/code> trick is not available for constructors. The code compiles, and then you get a linker error because the constructor definition is missing.<\/p>\n<p>The solution is the same as last time: Include the header file for the namespace you are consuming.<\/p>\n<p>In this case, you are trying to construct an <code>EventHandler<\/code>, which is part of the <code>winrt::<wbr \/>Windows::<wbr \/>Foundation<\/code> namespace, so the header file you need to include is <code>#include &lt;winrt\/<wbr \/>Windows.Foundation.h&gt;<\/code>.<\/p>\n<p><b>Bonus chatter<\/b>: I was able to come up with some error message metaprogramming to detect the missing header file at the point of construction, but it&#8217;s rather ugly and (worse) introduces another template for each runtime class and a template specialization for each namespace. Templates are responsible for a large amount of the compilation time of C++\/WinRT code, so introducing lots of new templates is something we try to avoid.<\/p>\n<p>But here it is anyway. Maybe you can find something cheaper.<\/p>\n<pre>\/\/ base.h\r\n\r\nnamespace winrt::impl\r\n{\r\n  template&lt;typename Header&gt;\r\n  struct include_check\r\n  {\r\n    static auto require()\r\n    {\r\n      static_assert(std::is_same_v&lt;Header, void&gt;,\r\n          \"Required header file missing (see error details for missing header file)\");\r\n    }\r\n  };\r\n}\r\n\r\n\/\/ Contoso.0.h\r\n\r\nnamespace winrt::impl::headers\r\n{\r\n    struct winrt_slash_contoso_dot_h;\r\n}\r\n\r\n\/\/ Contoso.2.h\r\n\r\nnamespace winrt::Contoso\r\n{\r\n  struct Widget : winrt::Contoso::IWidget\r\n  {\r\n    template&lt;typename Header = impl::headers::winrt_slash_contoso_dot_h,\r\n             typename = decltype(impl::include_check&lt;Header&gt;\r\n                                 ::require())&gt;\r\n    Widget();\r\n  };\r\n}\r\n\r\n\/\/ Contoso.h\r\n\r\nnamespace winrt::impl\r\n{\r\n  template&lt;&gt;\r\n  struct include_check&lt;headers::winrt_slash_contoso_dot_h&gt;\r\n  {\r\n    static void require();\r\n  };\r\n}\r\n\r\nnamespace winrt::Contoso\r\n{\r\n    template&lt;typename Header, typename&gt;\r\n    inline Widget::Widget()\r\n    {\r\n        \/* body *\/\r\n    }\r\n}\r\n<\/pre>\n<p>The resulting error is<\/p>\n<pre style=\"white-space: pre-wrap;\">\/\/ msvc\r\nerror C2338: Required header file missing (see error details for missing header file)\r\nnote: while compiling class template member function 'auto winrt::<wbr \/>impl::<wbr \/>include_check&lt;Header&gt;::<wbr \/>require(void)'\r\nwith\r\n[\r\n    Header=winrt::<wbr \/>impl::<wbr \/>headers::<wbr \/>winrt_slash_contoso_dot_h\r\n]\r\nnote: see reference to class template instantiation 'winrt::<wbr \/>impl::<wbr \/>include_check&lt;Header&gt;' being compiled\r\nwith\r\n[\r\n    Header=winrt::<wbr \/>impl::<wbr \/>headers::<wbr \/>winrt_slash_contoso_dot_h\r\n]\r\n\r\n\/\/ gcc\r\nIn instantiation of 'static auto winrt::<wbr \/>impl::<wbr \/>include_check&lt;Header&gt;::<wbr \/>require() [with Header = winrt::<wbr \/>impl::<wbr \/>headers::<wbr \/>winrt_slash_contoso_dot_h]':\r\nrequired by substitution of 'template&lt;<wbr \/>class Header, class&gt; winrt::<wbr \/>Contoso::<wbr \/>Widget::Blah() [with Header = winrt::<wbr \/>impl::<wbr \/>headers::<wbr \/>winrt_slash_contoso_dot_h; &lt;template-parameter-1-2&gt; = &lt;missing&gt;]'\r\nrequired from here\r\nerror: static assertion failed: Required header file missing (see error details for missing header file)\r\n|         static_assert(std::<wbr \/>is_same_v&lt;Header, void&gt;, \"Required header file missing (see error details for missing header file)\");\r\n|                       ~~~~~^~~~~~~~~~~~~~~~~~~~~~~\r\nnote: 'std::<wbr \/>is_same_v&lt;<wbr \/>winrt::<wbr \/>impl::<wbr \/>headers::<wbr \/>winrt_slash_contoso_dot_h, void&gt;' evaluates to false\r\n\r\n\/\/ clang\r\nerror: static assertion failed due to requirement 'std::<wbr \/>is_same_v&lt;<wbr \/>winrt::<wbr \/>impl::<wbr \/>headers::<wbr \/>winrt_slash_contoso_dot_h, void&gt;': Required header file missing (see error details for missing header file)\r\n        static_assert(std::<wbr \/>is_same_v&lt;Header, void&gt;, \"Required header file missing (see error details for missing header file)\");\r\n        ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\nnote: in instantiation of member function 'winrt::<wbr \/>impl::<wbr \/>include_check&lt;winrt::<wbr \/>impl::<wbr \/>headers::<wbr \/>winrt_slash_contoso_dot_h&gt;::<wbr \/>require' requested here\r\ntypename = decltype(impl::<wbr \/>include_check&lt;Header&gt;::<wbr \/>require())\r\n                                                 ^\r\nnote: in instantiation of default argument for 'Widget&lt;<wbr \/>impl::<wbr \/>headers::<wbr \/>winrt_slash_contoso_dot_h&gt;' required here\r\nWidget();\r\n^~~~~~\r\nnote: while substituting deduced template arguments into function template 'Widget' [with Header = (no value), $1 = (no value)]\r\n    winrt::<wbr \/>Contoso::<wbr \/>Widget w;\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>You forgot to include the namespace header file, didn&#8217;t you.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108800","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You forgot to include the namespace header file, didn&#8217;t you.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108800","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108800"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108800\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108800"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108800"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108800"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}