{"id":108448,"date":"2023-07-14T07:00:00","date_gmt":"2023-07-14T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108448"},"modified":"2023-07-13T12:11:56","modified_gmt":"2023-07-13T19:11:56","slug":"20230714-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230714-00\/?p=108448","title":{"rendered":"How to clone a Windows Runtime vector in the face of possible concurrent modification, part 3"},"content":{"rendered":"<p>Last time, we <a title=\"How to clone a Windows Runtime vector in the face of possible concurrent modification, part 2\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230713-00\/?p=108446\"> cloned a Windows Runtime vector in the face of possible concurrent modification<\/a>, but we ran into trouble with <code>std::vector&lt;bool&gt;<\/code>.<\/p>\n<p><a title=\"Of what use is a type-dependent expression that is always false?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200312-00\/?p=103556\"> As I noted some time ago<\/a>, the C++ language defines a specialization <code>std::vector&lt;bool&gt;<\/code> which represents a packed bit array, rather than defining a separate type like <code>std::bitvector<\/code>. This has made a lot of people very angry and <a href=\"https:\/\/isocpp.org\/blog\/2012\/11\/on-vectorbool\"> has been widely regarded<\/a> as a <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2005\/n1847.pdf\"> bad move<\/a>.<\/p>\n<p>In our case, the specialization of <code>std::vector&lt;bool&gt;<\/code> breaks our <code>clone_<wbr \/>as_<wbr \/>vector<\/code> function, since it needs a <code>winrt::<wbr \/>array_view&lt;bool&gt;<\/code>, which needs a C-style array of <code>bool<\/code> objects, not a packed bit array.<\/p>\n<p>We&#8217;ll have to detect the <code>bool<\/code> case in our function and substitute a <code>std::<wbr \/>unique_ptr&lt;bool[]&gt;<\/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.GetAt(0))&gt;\r\n{\r\n    using T = decltype(v.GetAt(0));\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">std::conditional_t&lt;         <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    std::is_same_v&lt;T, bool&gt;,<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    std::unique_ptr&lt;bool[]&gt;,<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">    std::vector&lt;T&gt;&gt;<\/span><span style=\"border-top: solid 1px currentcolor;\"> temp;   <\/span>\r\n    uint32_t expected;\r\n    uint32_t actual;\r\n    do {\r\n        expected = v.Size();\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">if constexpr (std::is_same_v&lt;T, bool&gt;) {             <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    temp = std::make_unique&lt;bool[]&gt;(expected + 1);   <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    actual = v.GetMany(0,                            <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">        winrt::array_view(temp.get(), expected + 1));<\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">} else {                                             <\/span>\r\n            temp.resize(expected + 1, <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220504-00\/?p=106569\">winrt_empty_value<\/a>&lt;T&gt;());\r\n            actual = v.GetMany(0, temp);\r\n        }\r\n    } while (actual &gt; expected);\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">if constexpr (std::is_same_v&lt;T, bool&gt;) {                <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    return std::vector(temp.get(), temp.get() + actual);<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">} else {                                                <\/span>\r\n        temp.erase(temp.begin() + actual, temp.end());\r\n        return temp;\r\n    }\r\n}\r\n<\/pre>\n<p>If the value type of the vector is a <code>bool<\/code>, then the <code>temp<\/code> changes to a <code>std::<wbr \/>unique_ptr&lt;bool[]&gt;<\/code>, and that means that we have to change how we resize the array (namely by replacing it with a new allocation) and how we generate the <code>array_view&lt;bool&gt;<\/code> (by using the pointer + size constructor).<\/p>\n<p>After the loop has captured the elements, we convert our C-style array of <code>bool<\/code> into a <code>std::<wbr \/>vector&lt;bool&gt;<\/code>.<\/p>\n<p>Now that I looked at it some more, it seems that there is barely any shared code at all. May as well just make it two functions glued together.<\/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.GetAt(0))&gt;\r\n{\r\n    using T = decltype(v.GetAt(0));\r\n    uint32_t expected;\r\n    uint32_t 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            expected = v.Size();\r\n            temp = std::make_unique&lt;bool[]&gt;(expected + 1);\r\n            actual = v.GetMany(0,\r\n                winrt::array_view(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; temp;\r\n        do {\r\n            expected = v.Size();\r\n            temp.resize(expected + 1, <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220504-00\/?p=106569\">winrt_empty_value<\/a>&lt;T&gt;());\r\n            actual = v.GetMany(0, temp);\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>Whew, we took care of the pesky <code>std::<wbr \/>vector&lt;bool&gt;<\/code>.<\/p>\n<p>Next time, we&#8217;ll look at the potential infinite loop and whether it offers a denial of service attack.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dealing with the pesky <CODE>std::vector&lt;bool&gt;<\/CODE>.<\/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-108448","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Dealing with the pesky <CODE>std::vector&lt;bool&gt;<\/CODE>.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108448","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=108448"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108448\/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=108448"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108448"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108448"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}