{"id":108466,"date":"2023-07-20T07:00:00","date_gmt":"2023-07-20T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108466"},"modified":"2023-07-20T05:49:41","modified_gmt":"2023-07-20T12:49:41","slug":"20230720-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230720-00\/?p=108466","title":{"rendered":"How to clone a Windows Runtime map in the face of possible concurrent modification, part 2"},"content":{"rendered":"<p>Although we&#8217;ve figured <a title=\"How to clone a Windows Runtime map in the face of possible concurrent modification, part 1\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230719-00\/?p=108462\"> how to clone a Windows Runtime map in the face of possible concurrent modification<\/a>, we&#8217;re still fiddling around with the best design for the function that does the cloning.<\/p>\n<p>Last time, we wrote a one-shot function that does everything, but maybe the answer is to not try to jam everything into one function.<\/p>\n<p>We can break things into two functions, one for using a <code>std::<wbr \/>map<\/code> and one for using a <code>std::<wbr \/>unordered_<wbr \/>map<\/code>. (This also avoids problems if the user tries to call the original function with something like <code>clone_<wbr \/>as_<wbr \/>map&lt;std::vector&lt;int&gt;&gt;<\/code>.)<\/p>\n<pre>template&lt;typename M&gt;\r\nauto clone_as_kvp_vector(M const&amp; m)\r\n{\r\n    using KVP = decltype(m.First().Current());\r\n    std::vector&lt;KVP&gt; pairs;\r\n    uint32_t expected;\r\n    uint32_t actual;\r\n    do {\r\n        expected = m.Size();\r\n        pairs.resize(expected + 1);\r\n        try {\r\n            actual = m.First().GetMany(pairs);\r\n        }\r\n        catch (winrt::hresult_changed_state const&amp;) {\r\n            continue;\r\n        }\r\n    } while (actual &gt; expected);\r\n    pairs.resize(actual);\r\n    return pairs;\r\n}\r\n<\/pre>\n<p>This worker function does the real work of cloning the Windows Runtime map into a C++ vector of <code>IKey\u00adValue\u00adPair<\/code> objects.<\/p>\n<p>The next step is to write some helpers to cut down on the repetitive typing that happens when you do <!-- backref: Reordering C++ template type parameters for usability purposes, and type deduction from the future --> type deduction from the future:<\/p>\n<pre>template&lt;typename Override, typename Fallback&gt;\r\nusing override_or_fallback_t =\r\n    std::conditional_t&lt;\r\n        std::is_same_v&lt;Override, void&gt;,\r\n        Fallback,\r\n        Override&gt;;\r\n\r\ntemplate&lt;typename M,\r\n    typename KeyOverride,\r\n    typename ValueOverride&gt;\r\nstruct inferred_runtime_map_traits\r\n{\r\n    using KVP = decltype(std::declval&lt;M&gt;().First().Current());\r\n\r\n    using Key = override_or_fallback_t&lt;\r\n        KeyOverride,\r\n        decltype(KVP().Key())&gt;;\r\n    using Value = override_or_fallback_t&lt;\r\n        ValueOverride,\r\n        decltype(KVP().Value())&gt;;\r\n};\r\n\r\ntemplate&lt;typename M,\r\n    typename KeyOverride,\r\n    typename ValueOverride,\r\n    typename CompareOverride&gt;\r\nstruct inferred_map_traits\r\n    {\r\n    using base = inferred_runtime_map_traits\r\n        &lt;M, KeyOverride, ValueOverride&gt;;\r\n    using Key = typename base::Key;\r\n    using Value = typename base::Value;\r\n    using Compare = override_or_fallback_t&lt;\r\n        CompareOverride, std::less&lt;Key&gt;&gt;;\r\n    using type = std::map\r\n        &lt;Key, Value, Compare&gt;;\r\n};\r\n\r\ntemplate&lt;typename M,\r\n    typename KeyOverride,\r\n    typename ValueOverride,\r\n    typename HashOverride,\r\n    typename KeyEqualOverride&gt;\r\nstruct inferred_unordered_map_traits\r\n{\r\n    using base = inferred_runtime_map_traits\r\n        &lt;M, KeyOverride, ValueOverride&gt;;\r\n    using Key = typename base::Key;\r\n    using Value = typename base::Value;\r\n    using Hash = override_or_fallback_t&lt;\r\n        HashOverride, std::hash&lt;Key&gt;&gt;;\r\n    using KeyEqual = override_or_fallback_t&lt;\r\n        KeyEqualOverride, std::equal_to&lt;Key&gt;&gt;;\r\n    using type = std::unordered_map\r\n        &lt;Key, Value, Hash, KeyEqual&gt;;\r\n};\r\n<\/pre>\n<p>We start with <code>override_<wbr \/>or_<wbr \/>fallback_t<\/code>, a type alias that encapsulates the &#8220;use the type provided if it is not <code>void<\/code>; otherwise use a fallback type.&#8221; We use this to build up an <code>inferred_<wbr \/>runtime_<wbr \/>map_<wbr \/>traits<\/code> which plucks out the <code>KVP<\/code>, <code>Key<\/code>, and <code>Value<\/code> from a Windows Runtime map. We also build an <code>inferred_<wbr \/>map_<wbr \/>traits<\/code> <code>inferred_<wbr \/>unordered_<wbr \/>map_<wbr \/>traits<\/code> which does the same for the additional template type parameters of a <code>std::<wbr \/>map<\/code> and <code>std::<wbr \/>unordered_<wbr \/>map<\/code>.<\/p>\n<p>We can use these traits types to save ourselves typing in the next few functions.<\/p>\n<pre>template&lt;typename Key = void,\r\n    typename Value = void,\r\n    typename Compare = void,\r\n    typename M,\r\n    typename Traits = inferred_map_traits\r\n        &lt;M, Key, Value, Compare&gt;&gt;\r\nauto clone_as_map(M const&amp; m,\r\n    typename Traits::Compare const&amp; compare = {})\r\n{\r\n    auto pairs = clone_as_kvp_vector(m);\r\n    typename Traits::type map(compare);\r\n    for (auto&amp;&amp; pair : pairs) {\r\n        map.emplace_hint(\r\n            map.end(), pair.Key(), pair.Value());\r\n    }\r\n    return map;\r\n}\r\n\r\ntemplate&lt;typename Key = void,\r\n    typename Value = void,\r\n    typename Hash = void,\r\n    typename KeyEqual = void,\r\n    typename M,\r\n    typename Traits = inferred_unordered_map_traits\r\n        &lt;M, Key, Value, Hash, KeyEqual&gt;&gt;\r\nauto clone_as_unordered_map(M const&amp; m,\r\n    typename Traits::Hash const&amp; hash = {},\r\n    typename Traits::KeyEqual const&amp; equal = {})\r\n{\r\n    auto pairs = clone_as_kvp_vector(m);\r\n    typename Traits::type map(\r\n        static_cast&lt;uint32_t&gt;(pairs.size()),\r\n        hash, equal);\r\n    for (auto&amp;&amp; pair : pairs) {\r\n        map.emplace(pair.Key(), pair.Value());\r\n    }\r\n    return map;\r\n}\r\n\r\ntemplate&lt;typename Key = void,\r\n    typename Value = void,\r\n    typename Compare = void,\r\n    typename M,\r\n    typename Traits = inferred_map_traits\r\n        &lt;M, Key, Value, Compare&gt;&gt;\r\nauto CloneMap(M const&amp; m,\r\n    typename Traits::Compare const&amp; compare = {})\r\n{\r\n    return winrt::multi_threaded_map(\r\n        clone_as_map&lt;Key, Value, Compare&gt;\r\n        (m, compare));\r\n}\r\n\r\ntemplate&lt;typename Key = void,\r\n    typename Value = void,\r\n    typename Hash = void,\r\n    typename KeyEqual = void,\r\n    typename M,\r\n    typename Traits = inferred_unordered_map_traits\r\n        &lt;M, Key, Value, Hash, KeyEqual&gt;&gt;\r\nauto CloneUnorderedMap(M const&amp; m,\r\n    typename Traits::Hash const&amp; hash = {},\r\n    typename Traits::KeyEqual const&amp; equal = {})\r\n{\r\n    return winrt::multi_threaded_map(\r\n        clone_as_unordered_map&lt;Key, Value, Hash, KeyEqual&gt;\r\n        (m, hash, equal));\r\n}\r\n<\/pre>\n<p>We front-load the template type parameters that belong to the map or unordered map, so that you can write something that looks almost natural.<\/p>\n<pre>\/\/ Create a std::unordered_map from strings to strings\r\n\/\/ but where the string keys are treated as case-insensitive.\r\nauto clone = clone_unordered_map&lt;\r\n    winrt::hstring, winrt::hstring,\r\n    case_insensitive_string_hash,\r\n    case_insensitive_string_equal&gt;(original);\r\n<\/pre>\n<p>Most of this wackiness was just C++ template metaprogramming nonsense.<\/p>\n<p>Notice that we used <code>std::<wbr \/>map::<wbr \/>emplace_hint<\/code> to optimize for the case that the keys are returned in sorted order.<\/p>\n<p>Next time, we&#8217;ll port this to C++\/CX.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Applying what we learned about vectors.<\/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-108466","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Applying what we learned about vectors.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108466","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=108466"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108466\/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=108466"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108466"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108466"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}