Show a patch.

GET /api/1.1/patches/17064/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 17064,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/17064/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/17064/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20220810002906.5406-4-laurent.pinchart@ideasonboard.com>",
    "date": "2022-08-10T00:29:05",
    "name": "[libcamera-devel,3/4] libcamera: controls: Store array control size in Control class",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "b05c1ba7723218767c202558a1b0a5beab59570d",
    "submitter": {
        "id": 2,
        "url": "https://patchwork.libcamera.org/api/1.1/people/2/?format=api",
        "name": "Laurent Pinchart",
        "email": "laurent.pinchart@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/17064/mbox/",
    "series": [
        {
            "id": 3401,
            "url": "https://patchwork.libcamera.org/api/1.1/series/3401/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=3401",
            "date": "2022-08-10T00:29:02",
            "name": "libcamera: Improve handling of fixed-size array controls",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/3401/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/17064/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/17064/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 7BFA3C3275\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 10 Aug 2022 00:29:25 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3231063331;\n\tWed, 10 Aug 2022 02:29:25 +0200 (CEST)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9128C63326\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Aug 2022 02:29:22 +0200 (CEST)",
            "from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2AC64481;\n\tWed, 10 Aug 2022 02:29:22 +0200 (CEST)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660091365;\n\tbh=R2E0Hd9RKLSF/Cq39wA7EfTlovoHLZaj8snJm2/wYeE=;\n\th=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=OFjl5PiTUTqmms1as/0ybPm8YVgTV2Ouz7sYgbFFmtVZzpAQTEGAVKKIhErhcGMY7\n\tHiPL9oKf5pH4ESu+xaZ6hDT3wk19i1Iw2dKstsZjUPdPX9mVFykyIvm6r2llmc9PSw\n\taHoRi8vCptC9AbucHZG3izO/95azR2gu3YXCrhoOf5dk8zW+QgMC5hvA0ozRazTYhH\n\tt+7tGPJw/a0htaJvh9NNzZko9cPZoUvfklMA5oXXTfBj2o7Tl0U73v1XZzoqRKffwb\n\twpvfxllIXyFKs9JiCblqn3LubjcckJGxMywybP27wJoFT9lFQIMbFqqmiLFGm6Ayrv\n\tESlZAKu7GGzwg==",
            "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1660091362;\n\tbh=R2E0Hd9RKLSF/Cq39wA7EfTlovoHLZaj8snJm2/wYeE=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=fEp40fGT57KJBcou0xErEhN4Ps/JA1Gs0OMcGWfwN6nLW1Giw+wh39AKB9dH6ZNVX\n\tp3yrozbM4kHmeRMO4n6JlEHjkowqghMMgFteQ7fmZSHCLR+U6PQ8atL9PnoTyQl/oj\n\tG2olVeOQ+w41ih7Hbf2pCdWPaB1gz1HiZOipY+Ys="
        ],
        "Authentication-Results": "lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"fEp40fGT\"; dkim-atps=neutral",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Wed, 10 Aug 2022 03:29:05 +0300",
        "Message-Id": "<20220810002906.5406-4-laurent.pinchart@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.35.1",
        "In-Reply-To": "<20220810002906.5406-1-laurent.pinchart@ideasonboard.com>",
        "References": "<20220810002906.5406-1-laurent.pinchart@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH 3/4] libcamera: controls: Store array\n\tcontrol size in Control class",
        "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>",
        "From": "Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>",
        "Reply-To": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>",
        "Errors-To": "libcamera-devel-bounces@lists.libcamera.org",
        "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"
    },
    "content": "Baside just identifying a control, the Control class enables usage of\nthe control type for metaprogramming. This is mainly used by the\nControlList get() and set() functions to type-check and cast the control\nvalue automatically at compilation time.\n\nExtend this with a new Size template argument for the Control class that\nspecifies the size of array controls. Use it already in the\nControlList::set() overload that takes an std::initializer list to\nconstruct the Span with an explicit size, enabling usage of the function\nfor fixed-size array controls.\n\nA possible future extension would be to pass the size to the ControlId\nconstructor and store it internally, enabling access to the size at\nruntime, for instance to perform validity checks.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n include/libcamera/controls.h | 20 +++++++++++--------\n src/libcamera/controls.cpp   | 37 +++++++++++++++++++++++++++---------\n utils/gen-controls.py        | 24 +++++++++++++++++++++--\n 3 files changed, 62 insertions(+), 19 deletions(-)",
    "diff": "diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h\nindex ebc168fc28b7..dd474a807d68 100644\n--- a/include/libcamera/controls.h\n+++ b/include/libcamera/controls.h\n@@ -8,6 +8,7 @@\n #pragma once\n \n #include <assert.h>\n+#include <limits>\n #include <optional>\n #include <set>\n #include <stdint.h>\n@@ -213,6 +214,8 @@ private:\n class ControlId\n {\n public:\n+\tstatic constexpr size_t dynamic_size = std::numeric_limits<std::size_t>::max();\n+\n \tControlId(unsigned int id, const std::string &name, ControlType type)\n \t\t: id_(id), name_(name), type_(type)\n \t{\n@@ -250,11 +253,12 @@ static inline bool operator!=(const ControlId &lhs, unsigned int rhs)\n \treturn !(lhs == rhs);\n }\n \n-template<typename T>\n+template<typename T, size_t Size = 0>\n class Control : public ControlId\n {\n public:\n \tusing type = T;\n+\tstatic constexpr size_t size = Size;\n \n \tControl(unsigned int id, const char *name)\n \t\t: ControlId(id, name, details::control_type<std::remove_cv_t<T>>::value)\n@@ -372,8 +376,8 @@ public:\n \n \tbool contains(unsigned int id) const;\n \n-\ttemplate<typename T>\n-\tstd::optional<T> get(const Control<T> &ctrl) const\n+\ttemplate<typename T, size_t Size>\n+\tstd::optional<T> get(const Control<T, Size> &ctrl) const\n \t{\n \t\tconst auto entry = controls_.find(ctrl.id());\n \t\tif (entry == controls_.end())\n@@ -383,8 +387,8 @@ public:\n \t\treturn val.get<T>();\n \t}\n \n-\ttemplate<typename T, typename V>\n-\tvoid set(const Control<T> &ctrl, const V &value)\n+\ttemplate<typename T, typename V, size_t Size>\n+\tvoid set(const Control<T, Size> &ctrl, const V &value)\n \t{\n \t\tControlValue *val = find(ctrl.id());\n \t\tif (!val)\n@@ -393,14 +397,14 @@ public:\n \t\tval->set<T>(value);\n \t}\n \n-\ttemplate<typename T, typename V>\n-\tvoid set(const Control<T> &ctrl, const std::initializer_list<V> &value)\n+\ttemplate<typename T, typename V, size_t Size>\n+\tvoid set(const Control<T, Size> &ctrl, const std::initializer_list<V> &value)\n \t{\n \t\tControlValue *val = find(ctrl.id());\n \t\tif (!val)\n \t\t\treturn;\n \n-\t\tval->set<T>(Span<const typename std::remove_cv_t<V>>{ value.begin(), value.size() });\n+\t\tval->set<T>(Span<const typename std::remove_cv_t<V>, Size>{ value.begin(), value.size() });\n \t}\n \n \tconst ControlValue &get(unsigned int id) const;\ndiff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp\nindex bc3db4f69388..24c836382f10 100644\n--- a/src/libcamera/controls.cpp\n+++ b/src/libcamera/controls.cpp\n@@ -384,6 +384,11 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n  * Control class for more information.\n  */\n \n+/**\n+ * \\var ControlId::dynamic_size\n+ * \\brief Size value for dynamic array controls\n+ */\n+\n /**\n  * \\fn ControlId::ControlId(unsigned int id, const std::string &name, ControlType type)\n  * \\brief Construct a ControlId instance\n@@ -431,12 +436,17 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n /**\n  * \\class Control\n  * \\brief Describe a control and its intrinsic properties\n+ * \\tparam T The control data type\n+ * \\tparam Size The number of elements (for array controls only)\n  *\n- * The Control class models a control exposed by an object. Its template type\n- * name T refers to the control data type, and allows functions that operate on\n- * control values to be defined as template functions using the same type T for\n- * the control value. See for instance how the ControlList::get() function\n- * returns a value corresponding to the type of the requested control.\n+ * The Control class models a control exposed by an object. Its template\n+ * paramter \\a T refers to the control data type, and allows functions that\n+ * operate on control values to be defined as template functions using the same\n+ * type T for the control value. See for instance how the ControlList::get()\n+ * function returns a value corresponding to the type of the requested control.\n+ *\n+ * Similarly, for array controls the template parameter \\a Size indicates the\n+ * number of elements in the array.\n  *\n  * While this class is the main means to refer to a control, the control\n  * identifying information is stored in the non-template base ControlId class.\n@@ -469,6 +479,15 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n  * \\brief The Control template type T\n  */\n \n+/**\n+ * \\var Control::size\n+ * \\brief The number of elements for array controls\n+ *\n+ * The \\a size reports the number of elements stored by an array Control. It is\n+ * equal to 0 for non-array controls, and to ControlId::dynamic_size for\n+ * variable-size controls.\n+ */\n+\n /**\n  * \\class ControlInfo\n  * \\brief Describe the limits of valid values for a Control\n@@ -943,7 +962,7 @@ bool ControlList::contains(unsigned int id) const\n }\n \n /**\n- * \\fn ControlList::get(const Control<T> &ctrl) const\n+ * \\fn ControlList::get(const Control<T, Size> &ctrl) const\n  * \\brief Get the value of control \\a ctrl\n  * \\param[in] ctrl The control\n  *\n@@ -956,7 +975,7 @@ bool ControlList::contains(unsigned int id) const\n  */\n \n /**\n- * \\fn ControlList::set(const Control<T> &ctrl, const V &value)\n+ * \\fn ControlList::set(const Control<T, Size> &ctrl, const V &value)\n  * \\brief Set the control \\a ctrl value to \\a value\n  * \\param[in] ctrl The control\n  * \\param[in] value The control value\n@@ -970,8 +989,8 @@ bool ControlList::contains(unsigned int id) const\n  */\n \n /**\n- * \\fn ControlList::set(const Control<T> &ctrl, const std::initializer_list<V> &value)\n- * \\copydoc ControlList::set(const Control<T> &ctrl, const V &value)\n+ * \\fn ControlList::set(const Control<T, Size> &ctrl, const std::initializer_list<V> &value)\n+ * \\copydoc ControlList::set(const Control<T, Size> &ctrl, const V &value)\n  */\n \n /**\ndiff --git a/utils/gen-controls.py b/utils/gen-controls.py\nindex bcfbeb7f9f17..a3b9c58beec6 100755\n--- a/utils/gen-controls.py\n+++ b/utils/gen-controls.py\n@@ -100,6 +100,10 @@ class Control(object):\n         ns = 'draft::' if self.is_draft else ''\n         return ns + self.__name\n \n+    @property\n+    def size(self):\n+        return self.__size\n+\n     @property\n     def type(self):\n         typ = self.__data.get('type')\n@@ -137,7 +141,7 @@ ${description}''')\n  * \\\\var ${name}\n ${description}\n  */''')\n-    def_template = string.Template('extern const Control<${type}> ${name}(${id_name}, \"${name}\");')\n+    def_template = string.Template('extern const Control<${type}${size}> ${name}(${id_name}, \"${name}\");')\n     enum_values_doc = string.Template('''/**\n  * \\\\var ${name}Values\n  * \\\\brief List of all $name supported values\n@@ -154,9 +158,17 @@ ${description}\n     for ctrl in controls:\n         id_name = snake_case(ctrl.name).upper()\n \n+        if ctrl.size is None:\n+            size = ''\n+        elif ctrl.size == 0:\n+            size = ', ControlId::dynamic_size'\n+        else:\n+            size = f', {ctrl.size}'\n+\n         info = {\n             'name': ctrl.name,\n             'type': ctrl.type,\n+            'size': size,\n             'description': format_description(ctrl.description),\n             'id_name': id_name,\n         }\n@@ -216,7 +228,7 @@ def generate_h(controls):\n     enum_template_start = string.Template('''enum ${name}Enum {''')\n     enum_value_template = string.Template('''\\t${name} = ${value},''')\n     enum_values_template = string.Template('''extern const std::array<const ControlValue, ${size}> ${name}Values;''')\n-    template = string.Template('''extern const Control<${type}> ${name};''')\n+    template = string.Template('''extern const Control<${type}${size}> ${name};''')\n \n     ctrls = []\n     draft_ctrls = []\n@@ -228,9 +240,17 @@ def generate_h(controls):\n \n         ids.append('\\t' + id_name + ' = ' + str(id_value) + ',')\n \n+        if ctrl.size is None:\n+            size = ''\n+        elif ctrl.size == 0:\n+            size = ', ControlId::dynamic_size'\n+        else:\n+            size = f', {ctrl.size}'\n+\n         info = {\n             'name': ctrl.name,\n             'type': ctrl.type,\n+            'size': size,\n         }\n \n         target_ctrls = ctrls\n",
    "prefixes": [
        "libcamera-devel",
        "3/4"
    ]
}