{"id":109795,"date":"2024-05-22T07:00:00","date_gmt":"2024-05-22T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109795"},"modified":"2024-05-22T16:11:00","modified_gmt":"2024-05-22T23:11:00","slug":"20240522-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240522-00\/?p=109795","title":{"rendered":"If you have to create a Windows Runtime Vector from C++\/WinRT, do it as late as possible"},"content":{"rendered":"<p>The Windows Runtime Vector is a language interoperability type, allowing your collection to be created from (say) C++, and manipulated by C#, JavaScript, Rust, Python, or any other language for which a Windows Runtime projection has been written.<\/p>\n<p>When implementing a function that returns a C++\/WinRT <code>IVector<\/code>, I often see this pattern:<\/p>\n<pre>winrt::IVector&lt;winrt::hstring&gt; GetNamesOfItems()\r\n{\r\n    auto v = winrt::multi_threaded_vector&lt;winrt::hstring&gt;();\r\n    for (auto&amp;&amp; item : GetItems()) {\r\n        v.Append(item.Name());\r\n    }\r\n    return v;\r\n}\r\n<\/pre>\n<p>The <code>multi_<wbr \/>threaded_<wbr \/>vector&lt;T&gt;<\/code> function creates an object that implements <code>IVector&lt;winrt::hstring&gt;<\/code> and returns that interface. We then use the <code>IVector&lt;T&gt;::Append()<\/code> method to append a bunch of objects to that vector before returning it.<\/p>\n<p>The gotcha is that <code>IVector<\/code> is a COM interface, so all method calls are virtual, which means that the compiler can&#8217;t optimize the method calls (unless it uses advanced techniques like devirtualization). Also, COM method calls must adhere to the Windows Runtime ABI, so C++-specific concepts like rvalue references cannot be used. The parameter to <code>v.Append()<\/code> is always copied; there is no concept of a &#8220;move&#8221; operation at the ABI.<\/p>\n<p>It&#8217;s better to operate with <code>std::vector<\/code> as long as possible, and convert it to <code>IVector<\/code> as a final step.<\/p>\n<pre>winrt::IVector&lt;winrt::hstring&gt; GetNamesOfItems()\r\n{\r\n    <span style=\"border: solid 1px currentcolor;\">auto v = std::vector&lt;winrt::hstring&gt;();<\/span>\r\n    for (auto&amp;&amp; item : GetItems()) {\r\n        v.<span style=\"border: solid 1px currentcolor;\">push_back<\/span>(item.Name());\r\n    }\r\n    return <span style=\"border: solid 1px currentcolor;\">winrt::multi_threaded_vector(std::move(v))<\/span>;\r\n}\r\n<\/pre>\n<p>This time, we are pushing elements onto the back of a <code>std::vector<\/code>, and the compiler can inline that operation. Furthermore, <code>push_back<\/code> has an overload that accepts rvalue references, so the <code>winrt::hstring<\/code> returned by <code>item.Name()<\/code> can be moved into the vector rather than copied.<\/p>\n<p>Staying with a <code>std::vector<\/code> also opens you up to other things. For example, you can sort them:<\/p>\n<pre>    std::sort(v.begin(), v.end());\r\n<\/pre>\n<p>This operation is not available for <code>IVector<\/code> because <code>std::sort<\/code> requires a random-access iterator, but <code>IVector<\/code>&#8216;s iterator does not meet the qualifications because the dereferencing oeprator returns a value, not a reference. There is no way to get a reference to a Windows Runtime vector element.<\/p>\n<p>And since we have a <code>std::vector<\/code>, we can use other tricks, like resizing the vector and writing to it, thereby removing the reallocation code from the inner loop.<\/p>\n<pre>winrt::IVector&lt;winrt::hstring&gt; GetNamesOfItems()\r\n{\r\n    auto v = std::vector&lt;winrt::hstring&gt;();\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">auto items = GetItems();                             <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">v.resize(items.size());                              <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">std::transform(items.begin(), items.end(), v.begin(),<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">    [](auto&amp;&amp; item) { return item.Name(); });        <\/span>\r\n    return winrt::multi_threaded_vector(std::move(v));\r\n}\r\n<\/pre>\n<p>Next time, we&#8217;ll see a special case of this that lets you avoid the vector, too.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Stay with the <CODE>std::vector<\/CODE> until you really need the Window Runtime Vector.<\/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-109795","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Stay with the <CODE>std::vector<\/CODE> until you really need the Window Runtime Vector.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109795","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=109795"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109795\/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=109795"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109795"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109795"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}