{"id":109729,"date":"2024-05-07T07:00:00","date_gmt":"2024-05-07T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109729"},"modified":"2024-05-08T05:53:34","modified_gmt":"2024-05-08T12:53:34","slug":"20240507-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240507-00\/?p=109729","title":{"rendered":"Awaiting a set of handles with a timeout, part 6: Capturing the handles efficiently"},"content":{"rendered":"<p>Last time, we <a title=\"Awaiting a set of handles with a timeout, part 5: Generalizing the awaiter\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240506-00\/?p=109727\"> created an awaiter that could await a <code>[first, last)<\/code> range of handles<\/a>. It did so by pushing onto a vector for each such handle, but this is inefficient in the case where the number of handles is known in advance. Let&#8217;s add a constructor for the case where the iterators support the subtraction operator.\u00b9<\/p>\n<pre>struct awaiter\r\n{\r\n    \u27e6 data members unchanged \u27e7\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">template&lt;typename Iter,                 <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    typename = std::void_t&lt;             <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        decltype(std::declval&lt;Iter&gt;() - <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">                 std::declval&lt;Iter&gt;())&gt;&gt;<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">awaiter(Iter first, Iter last,          <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    std::optional&lt;TimeSpan&gt; timeout,    <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    int) :                              <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    m_timeout(timeout)                  <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">{                                       <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    auto count = last - first;          <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    m_states.resize(count);             <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    for (auto&amp; s : m_states) {          <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        m_states.m_handle = *first;     <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">        ++first;                        <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    }                                   <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    create_waits();                     <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                       <\/span>\r\n\r\n    template&lt;typename Iter, <span style=\"border: solid 1px currentcolor;\">typename = void<\/span>&gt;\r\n    awaiter(Iter first, Iter last,\r\n        std::optional&lt;TimeSpan&gt; timeout,\r\n        <span style=\"border: solid 1px currentcolor;\">unsigned<\/span>) :\r\n        m_timeout(timeout)\r\n    {\r\n        std::transform(first, last, std::back_inserter(m_states),\r\n            [](HANDLE h) { state s; s.m_handle = h; return s; });\r\n        create_waits();\r\n    }\r\n\r\n    \u27e6 other methods unchanged \u27e7\r\n};\r\n\r\ntemplate&lt;typename Iter&gt;\r\nauto resume_on_all_signaled(Iter first, Iter last,\r\n    std::optional&lt;winrt::Windows::Foundation::TimeSpan&gt; timeout\r\n        = std::nullopt)\r\n{\r\n    return resume_all_awaiter(first, last, timeout, <span style=\"border: solid 1px currentcolor;\">0<\/span>);\r\n}\r\n<\/pre>\n<p>We add another constructor that is enabled via SFINAE if the iterator type supports subtraction. If so, then we use that subtraction operator to get the number of handles, then resize the vector directly to that size, so that we can fill in the handles without having to do any reallocating. This significantly reduces code size because the compiler doesn&#8217;t have to generate any resize logic.<\/p>\n<p>Note that if the iterator supports subtraction, then both constructors become available, so we use an unused parameter to steer the compiler toward the <code>int<\/code> version if it is available, since <code>int<\/code> is considered a better match for <code>0<\/code> than <code>unsigned<\/code>.<\/p>\n<p>\u00b9 Why do we look for a subtraction operator, rather than checking the iterator category for <code>random_<wbr \/>access_<wbr \/>iterator_<wbr \/>tag<\/code>? Because not all subtractable iterators satisfy the requirements of a random access iterator. In particular, dereferencing a random access iterator must produce a reference, which rules out things like <code>IVectorView<\/code> since the <code>Get\u00adAt<\/code> method returns a copy, not a reference. The C++ iterator library doesn&#8217;t have a built-in way to detect a random-access output iterator, so we have to make up our own.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SFINAE&#8217;ing the case of a random-access(ish) iterator.<\/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-109729","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>SFINAE&#8217;ing the case of a random-access(ish) iterator.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109729","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=109729"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109729\/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=109729"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109729"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109729"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}