{"id":12086,"url":"https://patchwork.libcamera.org/api/1.1/patches/12086/?format=json","web_url":"https://patchwork.libcamera.org/patch/12086/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20210423020932.2760-2-laurent.pinchart@ideasonboard.com>","date":"2021-04-23T02:09:30","name":"[libcamera-devel,PATCH/RFC,1/3] libcamera: utils: Add enumerate view for range-based for loops","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"7c15305f5d4dc7fee7d36daefe79b4ca0ca7eff3","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/1.1/people/2/?format=json","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"delegate":{"id":14,"url":"https://patchwork.libcamera.org/api/1.1/users/14/?format=json","username":"pinchartl","first_name":"Laurent","last_name":"Pinchart","email":"laurent.pinchart@ideasonboard.com"},"mbox":"https://patchwork.libcamera.org/patch/12086/mbox/","series":[{"id":1966,"url":"https://patchwork.libcamera.org/api/1.1/series/1966/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=1966","date":"2021-04-23T02:09:29","name":"libcamera: Simplify range-based for loop counters","version":1,"mbox":"https://patchwork.libcamera.org/series/1966/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/12086/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/12086/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 016D1BDB1A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 23 Apr 2021 02:09:46 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 72B8268875;\n\tFri, 23 Apr 2021 04:09:44 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8E00C68861\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 23 Apr 2021 04:09:41 +0200 (CEST)","from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 1587A564\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 23 Apr 2021 04:09:41 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"eefE2yGf\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1619143781;\n\tbh=gwxpHarXEzslxbmx64EyvNGa+6qWoCnFGckQuWyZRqM=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=eefE2yGfGrRUIaKSWZDIW+4JigCZnrHkbCpmx4ht5evwcKVnMa7EmrtvJQGaoUFPW\n\tJlD0JWeU2iKlWMtu24zdAXufi/rIdb6TC8O7h4/2Fmn8oPVeM/bb15WgFnNFD6SD3z\n\ttM+96L/AcDlGWjyaj86bWEL/lrWoSWQpP24bsVI4=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Fri, 23 Apr 2021 05:09:30 +0300","Message-Id":"<20210423020932.2760-2-laurent.pinchart@ideasonboard.com>","X-Mailer":"git-send-email 2.28.1","In-Reply-To":"<20210423020932.2760-1-laurent.pinchart@ideasonboard.com>","References":"<20210423020932.2760-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Subject":"[libcamera-devel] [PATCH/RFC 1/3] libcamera: utils: Add enumerate\n\tview for range-based for loops","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"Range-based for loops are handy and widely preferred in C++, but are\nlimited in their ability to replace for loops that require access to a\nloop counter.  The enumerate() function solves this problem by wrapping\nthe \\a iterable in an adapter that, when used as a range-expression,\nwill provide iterators whose value_type is a pair of index and value\nreference.\n\nThe iterable must support std::begin() and std::end(). This includes all\ncontainers provided by the standard C++ library, as well as C-style\narrays.\n\nA typical usage pattern would use structured binding to store the index\nand value in two separate variables:\n\nstd::vector<int> values = ...;\n\nfor (auto [index, value] : utils::enumerate(values)) {\n     ...\n}\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n include/libcamera/internal/utils.h | 86 ++++++++++++++++++++++++++++++\n src/libcamera/utils.cpp            | 29 ++++++++++\n test/utils.cpp                     | 59 ++++++++++++++++++++\n 3 files changed, 174 insertions(+)","diff":"diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h\nindex d0146b71727d..9372a75889ac 100644\n--- a/include/libcamera/internal/utils.h\n+++ b/include/libcamera/internal/utils.h\n@@ -9,12 +9,14 @@\n \n #include <algorithm>\n #include <chrono>\n+#include <iterator>\n #include <memory>\n #include <ostream>\n #include <sstream>\n #include <string>\n #include <string.h>\n #include <sys/time.h>\n+#include <utility>\n #include <vector>\n \n #ifndef __DOXYGEN__\n@@ -230,6 +232,90 @@ details::reverse_adapter<T> reverse(T &&iterable)\n \treturn { iterable };\n }\n \n+namespace details {\n+\n+template<typename Base>\n+class enumerate_iterator\n+{\n+private:\n+\tusing base_reference = typename std::iterator_traits<Base>::reference;\n+\n+public:\n+\tusing difference_type = typename std::iterator_traits<Base>::difference_type;\n+\tusing value_type = std::pair<const difference_type, base_reference>;\n+\tusing pointer = value_type *;\n+\tusing reference = value_type &;\n+\tusing iterator_category = typename std::iterator_traits<Base>::iterator_category;\n+\n+\texplicit enumerate_iterator(Base iter)\n+\t\t: current_(iter), pos_(0)\n+\t{\n+\t}\n+\n+\tenumerate_iterator &operator++()\n+\t{\n+\t\t++current_;\n+\t\t++pos_;\n+\t\treturn *this;\n+\t}\n+\n+\tbool operator!=(const enumerate_iterator &other) const\n+\t{\n+\t\treturn current_ != other.current_;\n+\t}\n+\n+\tvalue_type operator*() const\n+\t{\n+\t\treturn { pos_, *current_ };\n+\t}\n+\n+private:\n+\tBase current_;\n+\tdifference_type pos_;\n+};\n+\n+template<typename Base>\n+class enumerate_adapter\n+{\n+public:\n+\tusing iterator = enumerate_iterator<Base>;\n+\n+\tenumerate_adapter(Base begin, Base end)\n+\t\t: begin_(begin), end_(end)\n+\t{\n+\t}\n+\n+\titerator begin()\n+\t{\n+\t\treturn iterator{ begin_ };\n+\t}\n+\n+\titerator end()\n+\t{\n+\t\treturn iterator{ end_ };\n+\t}\n+\n+private:\n+\tBase begin_;\n+\tBase end_;\n+};\n+\n+} /* namespace details */\n+\n+template<typename T>\n+auto enumerate(T &iterable) -> details::enumerate_adapter<decltype(iterable.begin())>\n+{\n+\treturn { std::begin(iterable), std::end(iterable) };\n+}\n+\n+#ifndef __DOXYGEN__\n+template<typename T, size_t N>\n+auto enumerate(T (&iterable)[N]) -> details::enumerate_adapter<T *>\n+{\n+\treturn { std::begin(iterable), std::end(iterable) };\n+}\n+#endif\n+\n } /* namespace utils */\n \n } /* namespace libcamera */\ndiff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp\nindex c4098a74e0ab..ff9a5832b10e 100644\n--- a/src/libcamera/utils.cpp\n+++ b/src/libcamera/utils.cpp\n@@ -472,6 +472,35 @@ std::string libcameraSourcePath()\n  * loop, will cause the loop to iterate over the \\a iterable in reverse order\n  */\n \n+/**\n+ * \\fn enumerate(T &iterable)\n+ * \\brief Wrap an iterable to enumerate index and value in a range-based loop\n+ * \\param[in] iterable The iterable\n+ *\n+ * Range-based for loops are handy and widely preferred in C++, but are limited\n+ * in their ability to replace for loops that require access to a loop counter.\n+ * The enumerate() function solves this problem by wrapping the \\a iterable in\n+ * an adapter that, when used as a range-expression, will provide iterators\n+ * whose value_type is a pair of index and value reference.\n+ *\n+ * The iterable must support std::begin() and std::end(). This includes all\n+ * containers provided by the standard C++ library, as well as C-style arrays.\n+ *\n+ * A typical usage pattern would use structured binding to store the index and\n+ * value in two separate variables:\n+ *\n+ * \\code{.cpp}\n+ * std::vector<int> values = ...;\n+ *\n+ * for (auto [index, value] : utils::enumerate(values)) {\n+ * \t...\n+ * }\n+ * \\endcode\n+ *\n+ * \\return A value of unspecified type that, when used in a range-based for\n+ * loop, iterates over an indexed view of the \\a iterable\n+ */\n+\n } /* namespace utils */\n \n } /* namespace libcamera */\ndiff --git a/test/utils.cpp b/test/utils.cpp\nindex 08f293898fd9..7e24c71e4775 100644\n--- a/test/utils.cpp\n+++ b/test/utils.cpp\n@@ -12,6 +12,7 @@\n #include <vector>\n \n #include <libcamera/geometry.h>\n+#include <libcamera/span.h>\n \n #include \"libcamera/internal/utils.h\"\n \n@@ -73,6 +74,60 @@ protected:\n \t\treturn TestPass;\n \t}\n \n+\tint testEnumerate()\n+\t{\n+\t\tstd::vector<int> integers{ 1, 2, 3, 4, 5 };\n+\t\tint i = 0;\n+\n+\t\tfor (auto [index, value] : utils::enumerate(integers)) {\n+\t\t\tif (index != i || value != i + 1) {\n+\t\t\t\tcerr << \"utils::enumerate(<vector>) test failed: i=\" << i\n+\t\t\t\t     << \", index=\" << index << \", value=\" << value\n+\t\t\t\t     << std::endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n+\n+\t\t\t/* Verify that we can modify the value. */\n+\t\t\t--value;\n+\t\t\t++i;\n+\t\t}\n+\n+\t\tif (integers != std::vector<int>{ 0, 1, 2, 3, 4 }) {\n+\t\t\tcerr << \"Failed to modify container in enumerated range loop\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n+\n+\t\tSpan<const int> span{ integers };\n+\t\ti = 0;\n+\n+\t\tfor (auto [index, value] : utils::enumerate(span)) {\n+\t\t\tif (index != i || value != i) {\n+\t\t\t\tcerr << \"utils::enumerate(<span>) test failed: i=\" << i\n+\t\t\t\t     << \", index=\" << index << \", value=\" << value\n+\t\t\t\t     << std::endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n+\n+\t\t\t++i;\n+\t\t}\n+\n+\t\tconst int array[] = { 0, 2, 4, 6, 8 };\n+\t\ti = 0;\n+\n+\t\tfor (auto [index, value] : utils::enumerate(array)) {\n+\t\t\tif (index != i || value != i * 2) {\n+\t\t\t\tcerr << \"utils::enumerate(<array>) test failed: i=\" << i\n+\t\t\t\t     << \", index=\" << index << \", value=\" << value\n+\t\t\t\t     << std::endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n+\n+\t\t\t++i;\n+\t\t}\n+\n+\t\treturn TestPass;\n+\t}\n+\n \tint run()\n \t{\n \t\t/* utils::hex() test. */\n@@ -177,6 +232,10 @@ protected:\n \t\t\treturn TestFail;\n \t\t}\n \n+\t\t/* utils::enumerate() test. */\n+\t\tif (testEnumerate() != TestPass)\n+\t\t\treturn TestFail;\n+\n \t\treturn TestPass;\n \t}\n };\n","prefixes":["libcamera-devel","PATCH/RFC","1/3"]}