{"id":1130,"date":"2015-07-01T00:26:56","date_gmt":"2015-07-01T07:26:56","guid":{"rendered":"http:\/\/www.elbeno.com\/blog\/?p=1130"},"modified":"2015-07-01T23:08:31","modified_gmt":"2015-07-02T06:08:31","slug":"exercising-ranges-part-3","status":"publish","type":"post","link":"https:\/\/www.elbeno.com\/blog\/?p=1130","title":{"rendered":"Exercising Ranges (part 3)"},"content":{"rendered":"<p>(Start at <a href=\"https:\/\/www.elbeno.com\/blog\/?p=1118\">the beginning of the series<\/a> if you want more context.)<\/p>\n<p>So, I was going to implement <code>monoidal_zip<\/code>, and to do that, I would clone zip_with.hpp. So I did that. Eric&#8217;s a great library author, and the ranges code is pretty easy to read. For the most part I found it concise, well-expressed and clear. The parts I didn&#8217;t immediately understand were owing to my own (temporary) ignorance and not a result of murky code.<\/p>\n<p>Since <code>zip_with<\/code> takes N ranges and an n-ary function, it&#8217;s written with variadic templates and folding operations over parameter packs. A monoid by definition only uses a binary operation, so I immediately simplified that. Then I worked through each part of the file, adapting it to my use case.<\/p>\n<p><strong>Range cardinality<\/strong><\/p>\n<p>The first things in zip_with.hpp are a bunch of <code>constexpr<\/code> functions in a detail namespace, and then a cardinality calculation. Range cardinality turns out to be an enumeration with three members &#8211; <code>infinite<\/code> (-3), <code>unknown<\/code> (-2), and <code>finite<\/code> (-1) &#8211; and then a value of zero or above is a known cardinality such as might be obtained at compile-time from an array. <\/p>\n<p>Aside: I didn&#8217;t think about\/realise until much later that the cardinality of a vector isn&#8217;t known at compile time. Perhaps it could be in some cases &#8211; a shortcoming of the STL re <code>constexpr<\/code>?<\/p>\n<p>In the case of known cardinalities, <code>zip_with<\/code> uses the minimum of the N ranges; I needed the maximum. Simple, if verbose to deal with the other possible enum values.<\/p>\n<pre lang=\"cpp\">\r\nnamespace detail\r\n{\r\n  template<typename C1, typename C2>\r\n  using monoidal_zip_cardinality =\r\n    std::integral_constant<cardinality,\r\n      C1::value == infinite || C2::value == infinite ?\r\n        infinite :\r\n        C1::value == unknown || C2::value == unknown ?\r\n          unknown :\r\n          C1::value >= 0 && C2::value >= 0 ?\r\n            max_(C1::value, C2::value) :\r\n            finite>;\r\n} \/\/ namespace detail\r\n<\/pre>\n<p>(The function <code>max_<\/code> is one of Eric&#8217;s existing <code>constexpr<\/code> functions in zip_with.hpp.)<\/p>\n<p><strong>Cursors and sentinels<\/strong><\/p>\n<p>The main work in writing any view is of course, defining the <code>cursor<\/code> and the <code>sentinel<\/code> (internal representations of iterators: the view provides <code>begin_cursor<\/code> and <code>end_cursor<\/code> functions &#8211; in const and mutable forms &#8211; which return a <code>cursor<\/code> and a <code>sentinel<\/code> respectively).<\/p>\n<p>There are a few simplifying implementation patterns I found in several views that I adopted. Commonly the <code>cursor<\/code> and the <code>sentinel<\/code> are templated on a bool representing constness. And it&#8217;s also common for the cursor to need a pointer back to the range it&#8217;s iterating over, and that pointer needs the right constness, so we end up with code like:<\/p>\n<pre lang=\"cpp\">\r\ntemplate<typename Fun, typename R1, typename R2>\r\nstruct iter_monoidal_zip_view\r\n  : view_facade<iter_monoidal_zip_view<Fun, R1, R2>,\r\n                detail::monoidal_zip_cardinality<range_cardinality<R1>,\r\n                                                 range_cardinality<R2>>::value>\r\n{\r\n  ...\r\n  template <bool IsConst>\r\n  struct cursor\r\n  {\r\n    ...\r\n  private:\r\n    friend struct sentinel<IsConst>;\r\n    \/\/ add a const if I am const\r\n    template <typename T>\r\n    using constify_if = meta::apply<meta::add_const_if_c<IsConst>, T>;\r\n    \/\/ pointer to my view\r\n    using monoidal_zip_view_t = constify_if<iter_monoidal_zip_view>;\r\n    monoidal_zip_view_t *rng_;\r\n    \/\/ iterators I use\r\n    range_iterator_t<constify_if<R1>> it1_;\r\n    range_iterator_t<constify_if<R2>> it2_;\r\n    ...\r\n  };\r\n  ...\r\n};\r\n<\/pre>\n<p><strong>Satisfying the Concepts<\/strong><\/p>\n<p>I wanted a reversible range. That meant I needed my <code>cursor<\/code> to be bidirectional. Every cursor must implement <code>current<\/code>, <code>next<\/code> and <code>equal<\/code>, and of course a bidirectional cursor must implement <code>prev<\/code>:<\/p>\n<pre lang=\"cpp\">\r\nauto current() const;\r\nvoid next();\r\nvoid prev();\r\nbool equal(cursor const& that) const;\r\n<\/pre>\n<p>Every sentinel implements comparison with a cursor:<\/p>\n<pre lang=\"cpp\">\r\nbool equal(cursor<IsConst> const &pos) const;\r\n<\/pre>\n<p>And the range itself implements <code>begin_cursor<\/code> and <code>end_cursor<\/code>, with <code>end_cursor<\/code> returning either a cursor or a sentinel depending on whether the underlying ranges are bounded:<\/p>\n<pre lang=\"cpp\">\r\n  using are_bounded_t = meta::and_c<(bool) BoundedRange<R1>(),\r\n                                    (bool) BoundedRange<R2>()>;\r\n\r\n  cursor<false> begin_cursor()\r\n  {\r\n    return {*this, fun_, begin_tag{}};\r\n  }\r\n  meta::if_<are_bounded_t, cursor<false>, sentinel<false>>\r\n  end_cursor()\r\n  {\r\n    return {*this, fun_, end_tag{}};\r\n  }\r\n<\/pre>\n<p>(I omitted the const versions of these functions which use the <code>CONCEPT_REQUIRES<\/code> macro as a nice wrapper for <code>enable_if<\/code> style functionality.)<\/p>\n<p><strong>Nice conventions<\/strong><\/p>\n<p>In order that views work on containers-as-ranges, various views simply use <code>view::all<\/code> to turn whatever container is passed in into a range. If actual ranges are passed in, <code>view::all<\/code> just forwards them, unaffected. This is very handy.<\/p>\n<p>There is a convention in the ranges code that deals with reporting concept violations to the user: separate out the concept as a template alias, eg: <\/p>\n<pre lang=\"cpp\">\r\ntemplate<typename Fun, typename R1, typename R2>\r\nusing Concept = meta::and_<\r\n  InputRange<R1>, InputRange<R2>,\r\n  Callable<Fun, range_reference_t<R1> &&, range_reference_t<R2> &&>>;\r\n<\/pre>\n<p>And write the required function with an overloaded version which uses the CONCEPT_REQUIRES_ macro in an inverted sense from the &#8220;normal&#8221; version:<\/p>\n<pre lang=\"cpp\">\r\n\/\/ \"normal\" version\r\ntemplate<typename R1, typename R2, typename Fun,\r\n         CONCEPT_REQUIRES_(Concept<Fun, R1, R2>())>\r\nmonoidal_zip_view<Fun, all_t<R1>, all_t<R2>> operator()(\r\n    Fun fun, R1 && r1, R2 && r2) const\r\n{ ... }\r\n\r\n\/\/ \"concept failed\" version\r\ntemplate<typename Fun, typename R1, typename R2,\r\n         CONCEPT_REQUIRES_(!Concept<Fun, R1, R2>())>\r\nvoid operator()(Fun, R1 &&, R2 &&) const\r\n{ CONCEPT_ASSERT_MESSAGE(...); }\r\n<\/pre>\n<p><strong>The actual work<\/strong><\/p>\n<p>So, how does the cursor actually work? The idea is fairly simple. Keep an iterator to each range that the view is considering. Usually they advance together. When we run out of one range, keep track of the difference between the iterators, to enable reverse iteration. The rest is bookkeepping. (Error-prone, exacting bookkeeping: I didn&#8217;t get reverse iteration working properly until I gave the whole thing a proper workout with unit tests. Off-by-one errors, one of the three hardest things in computer science.)<\/p>\n<p>The <code>monoidal_zip<\/code> view&#8217;s <code>begin<\/code> is when both iterators are at their respective <code>begin<\/code>s, and the corresponding thing is true for <code>end<\/code>. You can find the implementation <a href=\"https:\/\/github.com\/elbeno\/power-series\/blob\/master\/src\/include\/monoidal_zip.hpp\">on github<\/a> &#8211; it&#8217;s too much for an already long post.<\/p>\n<p>Writing <code>current<\/code> is easy, we just need to check the iterators to do the monoid identity thing if needed:<\/p>\n<pre lang=\"cpp\">\r\nauto current() const\r\nRANGES_DECLTYPE_AUTO_RETURN_NOEXCEPT\r\n(\r\n  diff_ == 0 ?\r\n    fun_(it1_, it2_) :\r\n    diff_ > 0 ? *it1_ : *it2_\r\n)\r\n<\/pre>\n<p>And that&#8217;s it. Now we have <code>monoidal_zip<\/code>, which fulfils the needs of adding power series (and also subtracting them). It works on anything that&#8217;s a monoid, not just addition. And it&#8217;s reversible. In implementing this, I discovered that the result of reversing <code>zip_with<\/code> may be surprising: because the ranges are lazily traversed, effectively it&#8217;s as if the ranges were reversed <em>inside<\/em> the zip. If the ranges are different lengths, that may not be what you want, but if you think about it, it sort of has to be that way?<\/p>\n<p>After that lengthy workout, in the <a href=\"https:\/\/www.elbeno.com\/blog\/?p=1140\">next instalment<\/a> something easier: pretty-printing power series.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>(Start at the beginning of the series if you want more context.) So, I was going to implement monoidal_zip, and to do that, I would clone zip_with.hpp. So I did that. Eric&#8217;s a great library author, and the ranges code is pretty easy to read. For the most part I&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,8],"tags":[],"class_list":["post-1130","post","type-post","status-publish","format-standard","hentry","category-cpp","category-programming"],"_links":{"self":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1130","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1130"}],"version-history":[{"count":11,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1130\/revisions"}],"predecessor-version":[{"id":1190,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1130\/revisions\/1190"}],"wp:attachment":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1130"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1130"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1130"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}