{"id":3634,"date":"2018-08-03T15:39:52","date_gmt":"2018-08-03T14:39:52","guid":{"rendered":"http:\/\/roboblog.fatal-fury.de\/?p=3634"},"modified":"2018-08-06T15:49:58","modified_gmt":"2018-08-06T14:49:58","slug":"c-guns-sentinel-part-1-c17-p0184r0","status":"publish","type":"post","link":"http:\/\/roboblog.fatal-fury.de\/?p=3634","title":{"rendered":"C++ Guns: Multi-Arraylist with C++17 P0184R0 Sentinel"},"content":{"rendered":"<p><a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2016\/p0184r0.html\">P0184R0<\/a> Differing begin and end types in range-based for. <\/p>\n<blockquote><p>Sentinel - value to terminate a loop after it was read<\/p><\/blockquote>\n<p>Let me try something. A list implemented as array - Multiple lists stored in a single array.<br \/>\nThis can be helpful if the lists are very short e.g. less than 10 items.<br \/>\nFor this example, every list item is a single ID. Iterate over a list results in jumping around on the array.<br \/>\nI don't implement the part which insert new items in the list. Iterate over the lists is much more interesting.<\/p>\n<p>Example:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;vector&gt;\r\n#include &lt;iostream&gt;\r\n\r\nusing namespace std;\r\n\r\nconst int noIDvalue = -1;\r\n\r\nclass MultiArrayList {\r\npublic:\r\n    MultiArrayList() {\r\n        \/\/ fill with some test data\r\n        data.resize(10);\r\n        data&#x5B;0] = 9;\r\n        data&#x5B;1] = 3;\r\n        data&#x5B;2] = 1;\r\n        data&#x5B;3] = 8;\r\n        data&#x5B;4] = 7;\r\n        data&#x5B;5] = noIDvalue;\r\n        data&#x5B;6] = noIDvalue;\r\n        data&#x5B;7] = noIDvalue;\r\n        data&#x5B;8] = 5;\r\n        data&#x5B;9] = 4;\r\n\r\n        startIDs.resize(3);\r\n        startIDs&#x5B;0] = 0;\r\n        startIDs&#x5B;1] = 2;\r\n        startIDs&#x5B;2] = 6;\r\n    }\r\n\r\n\r\n    std::vector&lt;int&gt; data;\r\n    std::vector&lt;int&gt; startIDs;\r\n};\r\n\r\nint main() {\r\n    MultiArrayList multiList;\r\n    \/\/ print list content\r\n    for(int nextID : multiList.startIDs) {\r\n        cout &lt;&lt; &quot;List content: &quot;;\r\n        while(nextID != sentinelID) {\r\n            cout &lt;&lt; nextID &lt;&lt; &quot; &quot;;\r\n            nextID = multiList.data&#x5B;nextID];\r\n        }\r\n        cout &lt;&lt; &quot;\\n&quot;;\r\n    }\r\n}\r\n<\/pre>\n<blockquote><p>List content: 0 9 4 7<br \/>\nList content: 2 1 3 8 5<br \/>\nList content: 6\n<\/p><\/blockquote>\n<p>The hand crafted while loop is very ugly. I want something with a range. e.g.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n    for(size_t i=0; i &lt; multiList.size(); ++i) {\r\n        cout &lt;&lt; &quot;List content: &quot;;\r\n        for(const int ID : multiList.at(i)) {\r\n            std::cout &lt;&lt; ID &lt;&lt; &quot; &quot;;\r\n        }\r\n        cout &lt;&lt; &quot;\\n&quot;;\r\n    }\r\n<\/pre>\n<p>Lets see if I can implement this with the new C++17.<br \/>\nWe need <em>MultiArrayList::at<\/em> which simply returns a <em>MultiArrayListRange<\/em>. The range provide function <em>begin()<\/em> and <em>end()<\/em>. And we need <em>MultiArrayListIterator<\/em> which implements the iterator logic and of course <em>MultiArrayListSentinel<\/em> .<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n    auto MultiArrayList::at(size_t id) const {\r\n        return MultiArrayListRange(startIDs.at(i), data);\r\n    }\r\n\r\nclass MultiArrayListIterator {\r\npublic:\r\n    MultiArrayListIterator(const int startID, const std::vector&lt;int&gt;&amp; data)\r\n        : curID(startID), data(data)\r\n    {\r\n\r\n    }\r\n\r\n    void operator++() {\r\n        curID = data&#x5B;curID];\r\n    }\r\n\r\n    int operator*() const {\r\n        return curID;\r\n    }\r\n\r\nprivate:\r\n    int curID;\r\n    const std::vector&lt;int&gt;&amp; data;\r\n};\r\n\r\ntemplate&lt;int Delim&gt;\r\nstruct MultiArrayListSentinel {\r\n\r\n};\r\n\r\ntemplate&lt;int Delim&gt;\r\nbool operator!=(const MultiArrayListIterator&amp; lhs, const MultiArrayListSentinel&lt;Delim&gt;) {\r\n    return *lhs != Delim;\r\n}\r\n\r\nclass MultiArrayListRange {\r\npublic:\r\n    MultiArrayListRange(const int startID, const std::vector&lt;int&gt;&amp; data)\r\n        : startID(startID), data(data)\r\n    {\r\n\r\n    }\r\n\r\n    auto begin() {\r\n        return MultiArrayListIterator(startID, data);\r\n    }\r\n\r\n    auto end() {\r\n        return MultiArrayListSentinel&lt;noIDvalue&gt;();\r\n    }\r\n\r\nprivate:\r\n    const int startID;\r\n    const std::vector&lt;int&gt;&amp; data;\r\n};\r\n<\/pre>\n<p>Here we go. The core idea is the different return types of <em>begin()<\/em> and <em>end()<\/em>. One returns an iterator, the other a sentinel. The new <em>operator!=()<\/em> take both and check if the current ID is a <em>noIDvalue<\/em>, e.g. -1. If so, we are at the end of the list. <\/p>\n<p>The end iterator is never incremented, decremented, or dereferenced. Requiring it to be an iterator serves no practical purpose.<\/p>\n<p>As always: storing references is a bad idea. But everybody knows, iterators and now ranges are short-lived temporary objects which gets invalidated, so this is okay.<\/p>\n<p>PS: operator!= with sentinel is faster, because the right hand side is a compile time constant. It saves one assembler instruction (25%).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>P0184R0 Differing begin and end types in range-based for. Sentinel - value to terminate a loop after it was read Let me try something. A list implemented as array - Multiple lists stored in a single array. This can be helpful if the lists are very short e.g. less than 10 items. For this example, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[17],"class_list":["post-3634","post","type-post","status-publish","format-standard","hentry","category-allgemein","tag-cpp"],"_links":{"self":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3634","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3634"}],"version-history":[{"count":8,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3634\/revisions"}],"predecessor-version":[{"id":3648,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3634\/revisions\/3648"}],"wp:attachment":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3634"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3634"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3634"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}