{"id":108458,"date":"2023-07-18T07:00:00","date_gmt":"2023-07-18T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108458"},"modified":"2023-07-18T07:26:02","modified_gmt":"2023-07-18T14:26:02","slug":"20230718-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230718-00\/?p=108458","title":{"rendered":"How to clone a Windows Runtime vector in the face of possible concurrent modification, part 4"},"content":{"rendered":"<p>So far, we&#8217;ve looked at <a title=\"How to clone a Windows Runtime vector in the face of possible concurrent modification, part 3\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230714-00\/?p=108448\"> cloning a Windows Runtime vector in the face of possible concurrent modification<\/a>, using C++\/WinRT as our projection. Let&#8217;s extend our solution to C++\/CX.<\/p>\n<p>The initial translation to C++\/CX is straightforward. It&#8217;s pretty much a literal translation. We can take advantage of the fact that the default constructor for hat pointers is a null pointer, so we don&#8217;t have to use <code>winrt_<wbr \/>empty_<wbr \/>value()<\/code>.<\/p>\n<pre>template&lt;typename V&gt;\r\nauto clone_as_vector(V const&amp; v)\r\n-&gt; std::vector&lt;decltype(v-&gt;GetAt(0))&gt;\r\n{\r\n    using T = decltype(v-&gt;GetAt(0));\r\n    std::conditional_t&lt;\r\n        std::is_same_v&lt;T, bool&gt;,\r\n        std::unique_ptr&lt;bool[]&gt;,\r\n        std::vector&lt;T&gt;&gt; temp;\r\n    unsigned expected;\r\n    unsigned actual;\r\n    do\r\n    {\r\n        expected = v-&gt;Size;\r\n        if constexpr (std::is_same_v&lt;T, bool&gt;) {\r\n            temp = std::make_unique&lt;bool[]&gt;(expected + 1);\r\n            actual = v-&gt;GetMany(0, Platform::\r\n                ArrayReference&lt;T&gt;(temp.get(), expected + 1));                \r\n        }\r\n        else {\r\n            temp.resize(expected + 1);\r\n            actual = v-&gt;GetMany(0, Platform::\r\n                ArrayReference&lt;T&gt;(temp.data(), expected + 1));\r\n        }\r\n        }\r\n    } while (actual &gt; expected);\r\n    if constexpr (std::is_same_v&lt;T, bool&gt;) {\r\n        return std::vector(temp.get(), temp.get() + actual);\r\n    } else {\r\n        temp.resize(actual);\r\n        return temp;\r\n    }\r\n}\r\n\r\ntemplate&lt;typename V&gt;\r\nauto CloneVector(V const&amp; v)\r\n-&gt; Windows::Foundation::\r\n    Collections::IVector&lt;decltype(v-&gt;GetAt(0))&gt;^\r\n{\r\n    return ref new Platform::Collections::\r\n        Vector&lt;decltype(v-&gt;GetAt(0))&gt;(\r\n            clone_as_vector(v));\r\n}\r\n<\/pre>\n<p>Or if you prefer the total separation version:<\/p>\n<pre>template&lt;typename V&gt;\r\nauto clone_as_vector(V const&amp; v)\r\n-&gt; std::vector&lt;decltype(v-&gt;GetAt(0))&gt;\r\n{\r\n    using T = decltype(v-&gt;GetAt(0));\r\n    unsigned expected;\r\n    unsigned actual;\r\n    if constexpr (std::is_same_v&lt;T, bool&gt;) {\r\n        std::unique_ptr&lt;bool[]&gt; temp;\r\n        do\r\n        {\r\n            expected = v-&gt;Size;\r\n            temp = std::make_unique&lt;bool[]&gt;(expected + 1);\r\n            actual = v-&gt;GetMany(0, Platform::\r\n                ArrayReference&lt;T&gt;(temp.get(), expected + 1));                \r\n        } while (actual &gt; expected);\r\n        return std::vector(temp.get(), temp.get() + actual);\r\n    } else {\r\n        std::vector&lt;T&gt;&gt; temp;\r\n        do\r\n        {\r\n            expected = v-&gt;Size;\r\n            temp.resize(expected + 1);\r\n            actual = v-&gt;GetMany(0, Platform::\r\n                ArrayReference&lt;T&gt;(temp.data(), expected + 1));\r\n        } while (actual &gt; expected);\r\n        temp.erase(temp.begin() + actual, temp.end());\r\n        return temp;\r\n    }\r\n}\r\n<\/pre>\n<p>However, there&#8217;s an addition wrinkle to C++\/CX: C++\/WinRT uses the <code>==<\/code> operator to implement <code>IndexOf<\/code>, but C++\/CX lets you pass a custom equality comparer. (The default comparer is <code>std::equal_to&lt;T&gt;<\/code>, which uses <code>==<\/code>.) Therefore, to capture the full generality of <code>Vector<\/code>, we allow the caller of <code>Clone\u00adVector<\/code> to specify an equality comparer.<\/p>\n<p>Since we want to let the <code>V<\/code> be deduced, we resort to using <a title=\"Reordering C++ template type parameters for usability purposes, and type deduction from the future\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230609-00\/?p=108318\"> type deduction from the future<\/a>.\u00b9<\/p>\n<pre>template&lt;typename E = void, typename V&gt;\r\nauto CloneVector(V const&amp; v)\r\n-&gt; Windows::Foundation::\r\n    Collections::IVector&lt;decltype(v-&gt;GetAt(0))&gt;^\r\n{\r\n    using T = decltype(v-&gt;GetAt(0));\r\n    using ActualE = std::conditional_t&lt;\r\n        std::conditional_t&lt;std::is_same_v&lt;E, void&gt;,\r\n            std::equal_to&lt;T&gt;, E&gt;;\r\n    \r\n    return ref new Platform::Collections::\r\n        Vector&lt;T, ActualE&gt;(clone_as_vector(v));\r\n}\r\n<\/pre>\n<p>There&#8217;s another customization point that we haven&#8217;t bothered with: <code>std::<wbr \/>vector<\/code> lets you provide a custom allocator. I omitted that because custom allocators are rather esoteric, and besides, the <code>Platform::<wbr \/>Collections::<wbr \/>Vector<\/code> requires the provided <code>std::vector<\/code> to use the standard allocator.<\/p>\n<p><b>Bonus chatter<\/b>: You can see the implementation of the C++\/CX collection types in the header file <code>collection.h<\/code> that comes with the Visual C++ compiler. Other parts of the C++\/CX implementation can be found in <code>vccorlib.h<\/code>.<\/p>\n<p>I&#8217;m not smart. I just know how to read.<\/p>\n<p>\u00b9 An alternative to type deduction from the future is overloading:<\/p>\n<pre>template&lt;typename E, typename V&gt;\r\nauto CloneVector(V const&amp; v)\r\n-&gt; Windows::Foundation::\r\n    Collections::IVector&lt;decltype(v-&gt;GetAt(0))&gt;^\r\n{\r\n    using T = decltype(v-&gt;GetAt(0));\r\n    return ref new Platform::Collections::\r\n        Vector&lt;T, E&gt;(clone_as_vector(v));\r\n}\r\n\r\ntemplate&lt;typename V&gt;\r\nauto CloneVector(V const&amp; v)\r\n-&gt; Windows::Foundation::\r\n    Collections::IVector&lt;decltype(v-&gt;GetAt(0))&gt;^\r\n{\r\n    using T = decltype(v-&gt;GetAt(0));\r\n    return CloneVector&lt;std::equal_to&lt;T&gt;, V&gt;(v);\r\n}\r\n<\/pre>\n<p>The risk here is that somebody might want <code>E = V<\/code>.<\/p>\n<pre>\/\/ Error: Ambiguous call to overloaded function.\r\nCloneVector&lt;V&gt;(v);\r\n<\/pre>\n<p>This is trying to create a clone whose equality comparer is <code>V<\/code>, but the compiler reports this as an ambiguous call because it matches both templates.<\/p>\n<p>This could happen if <code>V<\/code> is a tree-like structure, so each element is a collection of other instances of itself.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Translating to C++\/CX and dealing with some quirks of that language.<\/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-108458","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Translating to C++\/CX and dealing with some quirks of that language.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108458","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=108458"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108458\/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=108458"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108458"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108458"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}