[{"id":37593,"web_url":"https://patchwork.libcamera.org/comment/37593/","msgid":"<a7bde25e-7b6e-4431-94c9-89b44bcc0602@ideasonboard.com>","date":"2026-01-13T09:43:18","subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 01. 13. 1:07 keltezéssel, Laurent Pinchart írta:\n> Add YamlObject::add() functions to add children to a list or dictionary\n> object. This will be used by the YamlParserContext to replace direct\n> access to YamlObject member variables to decouple the two classes.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>   include/libcamera/internal/yaml_parser.h |  3 ++\n>   src/libcamera/yaml_parser.cpp            | 56 ++++++++++++++++++++++++\n>   2 files changed, 59 insertions(+)\n> \n> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> index c98fe003c877..ede2c3de687c 100644\n> --- a/include/libcamera/internal/yaml_parser.h\n> +++ b/include/libcamera/internal/yaml_parser.h\n> @@ -196,6 +196,9 @@ public:\n>   \tbool contains(std::string_view key) const;\n>   \tconst YamlObject &operator[](std::string_view key) const;\n> \n> +\tYamlObject *add(std::unique_ptr<YamlObject> child);\n> +\tYamlObject *add(std::string key, std::unique_ptr<YamlObject> child);\n> +\n>   private:\n>   \tLIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)\n> \n> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> index 9b61d3e8fc50..7462e2d74179 100644\n> --- a/src/libcamera/yaml_parser.cpp\n> +++ b/src/libcamera/yaml_parser.cpp\n> @@ -448,6 +448,62 @@ const YamlObject &YamlObject::operator[](std::string_view key) const\n>   \treturn *iter->second;\n>   }\n> \n> +/**\n> + * \\brief Add a child object to a list\n> + * \\param[in] child The child object\n> + *\n> + * Append the \\a child node as the last element of this node's children list.\n> + * This node must be empty, in which case it is converted to the Type::List\n> + * type, or be a list. Otherwise, the \\a child is discarded and the function\n> + * returns a nullptr.\n> + *\n> + * \\return A pointer to the child object if successfully added, nullptr\n> + * otherwise\n> + */\n> +YamlObject *YamlObject::add(std::unique_ptr<YamlObject> child)\n> +{\n> +\tif (type_ == Type::Empty)\n> +\t\ttype_ = Type::List;\n> +\n> +\tif (type_ != Type::List)\n> +\t\treturn nullptr;\n> +\n> +\tValue &elem = list_.emplace_back(std::string{}, std::move(child));\n> +\treturn elem.value.get();\n> +}\n> +\n> +/**\n> + * \\brief Add a child object to a dictionary\n> + * \\param[in] key The dictionary key\n> + * \\param[in] child The child object\n> + *\n> + * Add the \\a child node with the given \\a key to this node's children. This\n> + * node must be empty, in which case it is converted to the Type::Dictionary\n> + * type, or be a dictionary. Otherwise, the \\a child is discarded and the\n> + * function returns a nullptr.\n> + *\n> + * Keys are unique. If a child with the same \\a key already exist, the \\a child\n> + * is discarded and the function returns a nullptr.\n\nI'm wondering if other behaviours, such as overwriting the child object make sense.\nAny thoughts? Or are these very specifically here to work with YamlParserContext?\n\n\n> + *\n> + * \\return A pointer to the child object if successfully added, nullptr\n> + * otherwise\n> + */\n> +YamlObject *YamlObject::add(std::string key, std::unique_ptr<YamlObject> child)\n> +{\n> +\tif (type_ == Type::Empty)\n> +\t\ttype_ = Type::Dictionary;\n> +\n> +\tif (type_ != Type::Dictionary)\n> +\t\treturn nullptr;\n> +\n> +\tif (dictionary_.find(key) != dictionary_.end())\n> +\t\treturn nullptr;\n> +\n> +\tValue &elem = list_.emplace_back(std::move(key), std::move(child));\n> +\tdictionary_.emplace(elem.key, elem.value.get());\n> +\treturn elem.value.get();\n\nSince `key` is already a string:\n\n   auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get());\n   if (!inserted)\n     return nullptr;\n\n   return list_.emplace_back(it->first, std::move(child)).value.get();\n\n\n> +}\n> +\n>   #ifndef __DOXYGEN__\n> \n>   class YamlParserContext\n> --\n> Regards,\n> \n> Laurent Pinchart\n>","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 DF627BDCBF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 13 Jan 2026 09:43:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DAC8961FC3;\n\tTue, 13 Jan 2026 10:43:24 +0100 (CET)","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 8AC8961F84\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jan 2026 10:43:22 +0100 (CET)","from [192.168.33.30] (185.221.143.114.nat.pool.zt.hu\n\t[185.221.143.114])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 6ABE750A;\n\tTue, 13 Jan 2026 10:42:56 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Q0004qL8\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1768297376;\n\tbh=m3LaUuLwyr5x+HEgR2dPIkfSsMNgVaKfgdf4Oac0XtU=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=Q0004qL8ceSXUlFJ0WCzBwZPk9F3AK0iYjXWF7BLiqP6JGQTJjx0j7k7oDqfTHaqu\n\taT7T5arhr2XPc2JbZNf3gB6eoEHSSKZ11nSG/ywH/itlN2Ks0hcI0ERigTBi1U//Me\n\tT3BVi1TBHxEFry9v5SP10mxDLRwBaO7gQjUGmFt4=","Message-ID":"<a7bde25e-7b6e-4431-94c9-89b44bcc0602@ideasonboard.com>","Date":"Tue, 13 Jan 2026 10:43:18 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20260113000808.15395-1-laurent.pinchart@ideasonboard.com>\n\t<uOU2iWgq9OsR65z0bhBmnJO1OJLZxB8hJMdi7gUaKdrtspGyw-pUZX959Od66Hc7otsMe7IPWXvUvRdnAOUMxQ==@protonmail.internalid>\n\t<20260113000808.15395-14-laurent.pinchart@ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260113000808.15395-14-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","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>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":37607,"web_url":"https://patchwork.libcamera.org/comment/37607/","msgid":"<20260113140355.GI6198@pendragon.ideasonboard.com>","date":"2026-01-13T14:03:55","subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Tue, Jan 13, 2026 at 10:43:18AM +0100, Barnabás Pőcze wrote:\n> 2026. 01. 13. 1:07 keltezéssel, Laurent Pinchart írta:\n> > Add YamlObject::add() functions to add children to a list or dictionary\n> > object. This will be used by the YamlParserContext to replace direct\n> > access to YamlObject member variables to decouple the two classes.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >   include/libcamera/internal/yaml_parser.h |  3 ++\n> >   src/libcamera/yaml_parser.cpp            | 56 ++++++++++++++++++++++++\n> >   2 files changed, 59 insertions(+)\n> > \n> > diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> > index c98fe003c877..ede2c3de687c 100644\n> > --- a/include/libcamera/internal/yaml_parser.h\n> > +++ b/include/libcamera/internal/yaml_parser.h\n> > @@ -196,6 +196,9 @@ public:\n> >   \tbool contains(std::string_view key) const;\n> >   \tconst YamlObject &operator[](std::string_view key) const;\n> > \n> > +\tYamlObject *add(std::unique_ptr<YamlObject> child);\n> > +\tYamlObject *add(std::string key, std::unique_ptr<YamlObject> child);\n> > +\n> >   private:\n> >   \tLIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)\n> > \n> > diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> > index 9b61d3e8fc50..7462e2d74179 100644\n> > --- a/src/libcamera/yaml_parser.cpp\n> > +++ b/src/libcamera/yaml_parser.cpp\n> > @@ -448,6 +448,62 @@ const YamlObject &YamlObject::operator[](std::string_view key) const\n> >   \treturn *iter->second;\n> >   }\n> > \n> > +/**\n> > + * \\brief Add a child object to a list\n> > + * \\param[in] child The child object\n> > + *\n> > + * Append the \\a child node as the last element of this node's children list.\n> > + * This node must be empty, in which case it is converted to the Type::List\n> > + * type, or be a list. Otherwise, the \\a child is discarded and the function\n> > + * returns a nullptr.\n> > + *\n> > + * \\return A pointer to the child object if successfully added, nullptr\n> > + * otherwise\n> > + */\n> > +YamlObject *YamlObject::add(std::unique_ptr<YamlObject> child)\n> > +{\n> > +\tif (type_ == Type::Empty)\n> > +\t\ttype_ = Type::List;\n> > +\n> > +\tif (type_ != Type::List)\n> > +\t\treturn nullptr;\n> > +\n> > +\tValue &elem = list_.emplace_back(std::string{}, std::move(child));\n> > +\treturn elem.value.get();\n> > +}\n> > +\n> > +/**\n> > + * \\brief Add a child object to a dictionary\n> > + * \\param[in] key The dictionary key\n> > + * \\param[in] child The child object\n> > + *\n> > + * Add the \\a child node with the given \\a key to this node's children. This\n> > + * node must be empty, in which case it is converted to the Type::Dictionary\n> > + * type, or be a dictionary. Otherwise, the \\a child is discarded and the\n> > + * function returns a nullptr.\n> > + *\n> > + * Keys are unique. If a child with the same \\a key already exist, the \\a child\n> > + * is discarded and the function returns a nullptr.\n> \n> I'm wondering if other behaviours, such as overwriting the child object make sense.\n> Any thoughts? Or are these very specifically here to work with YamlParserContext?\n\nThere should be no duplicate keys in YAML so both behaviours should work\nfine for this use case. I don't know at this point which behaviour would\nbe the most sensible for the majority of use cases.\n\n> > + *\n> > + * \\return A pointer to the child object if successfully added, nullptr\n> > + * otherwise\n> > + */\n> > +YamlObject *YamlObject::add(std::string key, std::unique_ptr<YamlObject> child)\n> > +{\n> > +\tif (type_ == Type::Empty)\n> > +\t\ttype_ = Type::Dictionary;\n> > +\n> > +\tif (type_ != Type::Dictionary)\n> > +\t\treturn nullptr;\n> > +\n> > +\tif (dictionary_.find(key) != dictionary_.end())\n> > +\t\treturn nullptr;\n> > +\n> > +\tValue &elem = list_.emplace_back(std::move(key), std::move(child));\n> > +\tdictionary_.emplace(elem.key, elem.value.get());\n> > +\treturn elem.value.get();\n> \n> Since `key` is already a string:\n> \n>    auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get());\n>    if (!inserted)\n>      return nullptr;\n> \n>    return list_.emplace_back(it->first, std::move(child)).value.get();\n\nI assume you meant that this would replace all code from\ndictionary_.find() onwards. It seems fine, I'll do that in v2.\n\n> > +}\n> > +\n> >   #ifndef __DOXYGEN__\n> > \n> >   class YamlParserContext","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 29566BDCBF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 13 Jan 2026 14:04:18 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 7065461FA3;\n\tTue, 13 Jan 2026 15:04:17 +0100 (CET)","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 ED01761FA0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jan 2026 15:04:15 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-152.bb.dnainternet.fi\n\t[81.175.209.152])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id A13F750A;\n\tTue, 13 Jan 2026 15:03:49 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"j3mMnnc1\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1768313029;\n\tbh=FAU9QyQrKhKdaAbt1Zak3VDIHh5Ixo0A/AlYxDuWG8U=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=j3mMnnc165UvcoDcf6e7l+kTvNnaOn8ILG/rSGUe7yQEqEVvzxt+mU3I1Kr/hhscR\n\taAYBcsYtHvlhimdAHM32UYyAHWHFpgHlHZG8i3wkBeutcG2htWTCJ76F+NZr8JVoRu\n\tFgsj3sUxMMrofAxdtXVbbcRv5ozTrWSysgZpm32I=","Date":"Tue, 13 Jan 2026 16:03:55 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","Message-ID":"<20260113140355.GI6198@pendragon.ideasonboard.com>","References":"<20260113000808.15395-1-laurent.pinchart@ideasonboard.com>\n\t<uOU2iWgq9OsR65z0bhBmnJO1OJLZxB8hJMdi7gUaKdrtspGyw-pUZX959Od66Hc7otsMe7IPWXvUvRdnAOUMxQ==@protonmail.internalid>\n\t<20260113000808.15395-14-laurent.pinchart@ideasonboard.com>\n\t<a7bde25e-7b6e-4431-94c9-89b44bcc0602@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<a7bde25e-7b6e-4431-94c9-89b44bcc0602@ideasonboard.com>","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>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":37716,"web_url":"https://patchwork.libcamera.org/comment/37716/","msgid":"<20260119000728.GD7759@pendragon.ideasonboard.com>","date":"2026-01-19T00:07:28","subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Tue, Jan 13, 2026 at 04:03:55PM +0200, Laurent Pinchart wrote:\n> On Tue, Jan 13, 2026 at 10:43:18AM +0100, Barnabás Pőcze wrote:\n> > 2026. 01. 13. 1:07 keltezéssel, Laurent Pinchart írta:\n> > > Add YamlObject::add() functions to add children to a list or dictionary\n> > > object. This will be used by the YamlParserContext to replace direct\n> > > access to YamlObject member variables to decouple the two classes.\n> > > \n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > >   include/libcamera/internal/yaml_parser.h |  3 ++\n> > >   src/libcamera/yaml_parser.cpp            | 56 ++++++++++++++++++++++++\n> > >   2 files changed, 59 insertions(+)\n> > > \n> > > diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> > > index c98fe003c877..ede2c3de687c 100644\n> > > --- a/include/libcamera/internal/yaml_parser.h\n> > > +++ b/include/libcamera/internal/yaml_parser.h\n> > > @@ -196,6 +196,9 @@ public:\n> > >   \tbool contains(std::string_view key) const;\n> > >   \tconst YamlObject &operator[](std::string_view key) const;\n> > > \n> > > +\tYamlObject *add(std::unique_ptr<YamlObject> child);\n> > > +\tYamlObject *add(std::string key, std::unique_ptr<YamlObject> child);\n> > > +\n> > >   private:\n> > >   \tLIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)\n> > > \n> > > diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> > > index 9b61d3e8fc50..7462e2d74179 100644\n> > > --- a/src/libcamera/yaml_parser.cpp\n> > > +++ b/src/libcamera/yaml_parser.cpp\n> > > @@ -448,6 +448,62 @@ const YamlObject &YamlObject::operator[](std::string_view key) const\n> > >   \treturn *iter->second;\n> > >   }\n> > > \n> > > +/**\n> > > + * \\brief Add a child object to a list\n> > > + * \\param[in] child The child object\n> > > + *\n> > > + * Append the \\a child node as the last element of this node's children list.\n> > > + * This node must be empty, in which case it is converted to the Type::List\n> > > + * type, or be a list. Otherwise, the \\a child is discarded and the function\n> > > + * returns a nullptr.\n> > > + *\n> > > + * \\return A pointer to the child object if successfully added, nullptr\n> > > + * otherwise\n> > > + */\n> > > +YamlObject *YamlObject::add(std::unique_ptr<YamlObject> child)\n> > > +{\n> > > +\tif (type_ == Type::Empty)\n> > > +\t\ttype_ = Type::List;\n> > > +\n> > > +\tif (type_ != Type::List)\n> > > +\t\treturn nullptr;\n> > > +\n> > > +\tValue &elem = list_.emplace_back(std::string{}, std::move(child));\n> > > +\treturn elem.value.get();\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Add a child object to a dictionary\n> > > + * \\param[in] key The dictionary key\n> > > + * \\param[in] child The child object\n> > > + *\n> > > + * Add the \\a child node with the given \\a key to this node's children. This\n> > > + * node must be empty, in which case it is converted to the Type::Dictionary\n> > > + * type, or be a dictionary. Otherwise, the \\a child is discarded and the\n> > > + * function returns a nullptr.\n> > > + *\n> > > + * Keys are unique. If a child with the same \\a key already exist, the \\a child\n> > > + * is discarded and the function returns a nullptr.\n> > \n> > I'm wondering if other behaviours, such as overwriting the child object make sense.\n> > Any thoughts? Or are these very specifically here to work with YamlParserContext?\n> \n> There should be no duplicate keys in YAML so both behaviours should work\n> fine for this use case. I don't know at this point which behaviour would\n> be the most sensible for the majority of use cases.\n> \n> > > + *\n> > > + * \\return A pointer to the child object if successfully added, nullptr\n> > > + * otherwise\n> > > + */\n> > > +YamlObject *YamlObject::add(std::string key, std::unique_ptr<YamlObject> child)\n> > > +{\n> > > +\tif (type_ == Type::Empty)\n> > > +\t\ttype_ = Type::Dictionary;\n> > > +\n> > > +\tif (type_ != Type::Dictionary)\n> > > +\t\treturn nullptr;\n> > > +\n> > > +\tif (dictionary_.find(key) != dictionary_.end())\n> > > +\t\treturn nullptr;\n> > > +\n> > > +\tValue &elem = list_.emplace_back(std::move(key), std::move(child));\n> > > +\tdictionary_.emplace(elem.key, elem.value.get());\n> > > +\treturn elem.value.get();\n> > \n> > Since `key` is already a string:\n> > \n> >    auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get());\n> >    if (!inserted)\n> >      return nullptr;\n> > \n> >    return list_.emplace_back(it->first, std::move(child)).value.get();\n> \n> I assume you meant that this would replace all code from\n> dictionary_.find() onwards. It seems fine, I'll do that in v2.\n\nIn file included from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/x86_64-pc-linux-gnu/bits/c++allocator.h:33,\n                 from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/allocator.h:46,\n                 from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/string:43,\n                 from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/locale_classes.h:40,\n                 from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/ios_base.h:41,\n                 from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/streambuf:43,\n                 from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/streambuf_iterator.h:35,\n                 from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/iterator:66,\n                 from ../../include/libcamera/internal/yaml_parser.h:10,\n                 from ../../src/libcamera/yaml_parser.cpp:8:\n/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/new_allocator.h: In instantiation of ‘void std::__new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = libcamera::YamlObject::Value; _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<libcamera::YamlObject, std::default_delete<libcamera::YamlObject> >}; _Tp = libcamera::YamlObject::Value]’:\n/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/alloc_traits.h:538:17:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(allocator_type&, _Up*, _Args&& ...) [with _Up = libcamera::YamlObject::Value; _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<libcamera::YamlObject, std::default_delete<libcamera::YamlObject> >}; _Tp = libcamera::YamlObject::Value; allocator_type = std::allocator<libcamera::YamlObject::Value>]’\n/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/vector.tcc:117:30:   required from ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<libcamera::YamlObject, std::default_delete<libcamera::YamlObject> >}; _Tp = libcamera::YamlObject::Value; _Alloc = std::allocator<libcamera::YamlObject::Value>; reference = libcamera::YamlObject::Value&]’\n../../src/libcamera/yaml_parser.cpp:491:27:   required from here\n/usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/new_allocator.h:191:11: error: cannot bind rvalue reference of type ‘std::string&&’ {aka ‘std::__cxx11::basic_string<char>&&’} to lvalue of type ‘const std::__cxx11::basic_string<char>’\n  191 |         { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }\n      |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n../../include/libcamera/internal/yaml_parser.h:31:37: note:   initializing argument 1 of ‘libcamera::YamlObject::Value::Value(std::string&&, std::unique_ptr<libcamera::YamlObject>&&)’\n   31 |                 Value(std::string &&k, std::unique_ptr<YamlObject> &&v)\n      |                       ~~~~~~~~~~~~~~^\n\nI'll drop the rvalue reference from the YamlObject::Value constructor.\n\n> > > +}\n> > > +\n> > >   #ifndef __DOXYGEN__\n> > > \n> > >   class YamlParserContext","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 5678CBDCBE\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 19 Jan 2026 00:07:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 62EEC61FBC;\n\tMon, 19 Jan 2026 01:07:53 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3C07461F61\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 19 Jan 2026 01:07:51 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-152.bb.dnainternet.fi\n\t[81.175.209.152])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id DF0A92D9;\n\tMon, 19 Jan 2026 01:07:20 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"owfYOPhK\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1768781241;\n\tbh=YpGY1572Ru5e1xWuC9ECtVtXYS9IqC1/pt4AL1eCeoY=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=owfYOPhKvs0SszLXQB3bg4fNuzoj7CP3FLYXE7x9MQtXee3ol3tgTSTiE0gujg6Vi\n\tgpZ/mZgIi1YHcp5vPyRAq8X24TSTlBH9Kjl5gy8qdiekq9810Ix3E2PaGmL4+Ks0Ra\n\tJ3VPW/yVcADgflpf7T854Cgi+FKG22006GwGvzz4=","Date":"Mon, 19 Jan 2026 02:07:28 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","Message-ID":"<20260119000728.GD7759@pendragon.ideasonboard.com>","References":"<20260113000808.15395-1-laurent.pinchart@ideasonboard.com>\n\t<uOU2iWgq9OsR65z0bhBmnJO1OJLZxB8hJMdi7gUaKdrtspGyw-pUZX959Od66Hc7otsMe7IPWXvUvRdnAOUMxQ==@protonmail.internalid>\n\t<20260113000808.15395-14-laurent.pinchart@ideasonboard.com>\n\t<a7bde25e-7b6e-4431-94c9-89b44bcc0602@ideasonboard.com>\n\t<20260113140355.GI6198@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20260113140355.GI6198@pendragon.ideasonboard.com>","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>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":37718,"web_url":"https://patchwork.libcamera.org/comment/37718/","msgid":"<17125812-6dce-4b08-badc-d51b11566fae@ideasonboard.com>","date":"2026-01-19T08:15:18","subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 01. 19. 1:07 keltezéssel, Laurent Pinchart írta:\n> On Tue, Jan 13, 2026 at 04:03:55PM +0200, Laurent Pinchart wrote:\n>> On Tue, Jan 13, 2026 at 10:43:18AM +0100, Barnabás Pőcze wrote:\n>>> 2026. 01. 13. 1:07 keltezéssel, Laurent Pinchart írta:\n>>>> Add YamlObject::add() functions to add children to a list or dictionary\n>>>> object. This will be used by the YamlParserContext to replace direct\n>>>> access to YamlObject member variables to decouple the two classes.\n>>>>\n>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n>>>> ---\n>>>>    include/libcamera/internal/yaml_parser.h |  3 ++\n>>>>    src/libcamera/yaml_parser.cpp            | 56 ++++++++++++++++++++++++\n>>>>    2 files changed, 59 insertions(+)\n>>>>\n>>>> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n>>>> index c98fe003c877..ede2c3de687c 100644\n>>>> --- a/include/libcamera/internal/yaml_parser.h\n>>>> +++ b/include/libcamera/internal/yaml_parser.h\n>>>> @@ -196,6 +196,9 @@ public:\n>>>>    \tbool contains(std::string_view key) const;\n>>>>    \tconst YamlObject &operator[](std::string_view key) const;\n>>>>\n>>>> +\tYamlObject *add(std::unique_ptr<YamlObject> child);\n>>>> +\tYamlObject *add(std::string key, std::unique_ptr<YamlObject> child);\n>>>> +\n>>>>    private:\n>>>>    \tLIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)\n>>>>\n>>>> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n>>>> index 9b61d3e8fc50..7462e2d74179 100644\n>>>> --- a/src/libcamera/yaml_parser.cpp\n>>>> +++ b/src/libcamera/yaml_parser.cpp\n>>>> @@ -448,6 +448,62 @@ const YamlObject &YamlObject::operator[](std::string_view key) const\n>>>>    \treturn *iter->second;\n>>>>    }\n>>>>\n>>>> +/**\n>>>> + * \\brief Add a child object to a list\n>>>> + * \\param[in] child The child object\n>>>> + *\n>>>> + * Append the \\a child node as the last element of this node's children list.\n>>>> + * This node must be empty, in which case it is converted to the Type::List\n>>>> + * type, or be a list. Otherwise, the \\a child is discarded and the function\n>>>> + * returns a nullptr.\n>>>> + *\n>>>> + * \\return A pointer to the child object if successfully added, nullptr\n>>>> + * otherwise\n>>>> + */\n>>>> +YamlObject *YamlObject::add(std::unique_ptr<YamlObject> child)\n>>>> +{\n>>>> +\tif (type_ == Type::Empty)\n>>>> +\t\ttype_ = Type::List;\n>>>> +\n>>>> +\tif (type_ != Type::List)\n>>>> +\t\treturn nullptr;\n>>>> +\n>>>> +\tValue &elem = list_.emplace_back(std::string{}, std::move(child));\n>>>> +\treturn elem.value.get();\n>>>> +}\n>>>> +\n>>>> +/**\n>>>> + * \\brief Add a child object to a dictionary\n>>>> + * \\param[in] key The dictionary key\n>>>> + * \\param[in] child The child object\n>>>> + *\n>>>> + * Add the \\a child node with the given \\a key to this node's children. This\n>>>> + * node must be empty, in which case it is converted to the Type::Dictionary\n>>>> + * type, or be a dictionary. Otherwise, the \\a child is discarded and the\n>>>> + * function returns a nullptr.\n>>>> + *\n>>>> + * Keys are unique. If a child with the same \\a key already exist, the \\a child\n>>>> + * is discarded and the function returns a nullptr.\n>>>\n>>> I'm wondering if other behaviours, such as overwriting the child object make sense.\n>>> Any thoughts? Or are these very specifically here to work with YamlParserContext?\n>>\n>> There should be no duplicate keys in YAML so both behaviours should work\n>> fine for this use case. I don't know at this point which behaviour would\n>> be the most sensible for the majority of use cases.\n>>\n>>>> + *\n>>>> + * \\return A pointer to the child object if successfully added, nullptr\n>>>> + * otherwise\n>>>> + */\n>>>> +YamlObject *YamlObject::add(std::string key, std::unique_ptr<YamlObject> child)\n>>>> +{\n>>>> +\tif (type_ == Type::Empty)\n>>>> +\t\ttype_ = Type::Dictionary;\n>>>> +\n>>>> +\tif (type_ != Type::Dictionary)\n>>>> +\t\treturn nullptr;\n>>>> +\n>>>> +\tif (dictionary_.find(key) != dictionary_.end())\n>>>> +\t\treturn nullptr;\n>>>> +\n>>>> +\tValue &elem = list_.emplace_back(std::move(key), std::move(child));\n>>>> +\tdictionary_.emplace(elem.key, elem.value.get());\n>>>> +\treturn elem.value.get();\n>>>\n>>> Since `key` is already a string:\n>>>\n>>>     auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get());\n>>>     if (!inserted)\n>>>       return nullptr;\n>>>\n>>>     return list_.emplace_back(it->first, std::move(child)).value.get();\n>>\n>> I assume you meant that this would replace all code from\n>> dictionary_.find() onwards. It seems fine, I'll do that in v2.\n> \n> In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/x86_64-pc-linux-gnu/bits/c++allocator.h:33,\n>                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/allocator.h:46,\n>                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/string:43,\n>                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/locale_classes.h:40,\n>                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/ios_base.h:41,\n>                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/streambuf:43,\n>                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/streambuf_iterator.h:35,\n>                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/iterator:66,\n>                   from ../../include/libcamera/internal/yaml_parser.h:10,\n>                   from ../../src/libcamera/yaml_parser.cpp:8:\n> /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/new_allocator.h: In instantiation of ‘void std::__new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = libcamera::YamlObject::Value; _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<libcamera::YamlObject, std::default_delete<libcamera::YamlObject> >}; _Tp = libcamera::YamlObject::Value]’:\n> /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/alloc_traits.h:538:17:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(allocator_type&, _Up*, _Args&& ...) [with _Up = libcamera::YamlObject::Value; _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<libcamera::YamlObject, std::default_delete<libcamera::YamlObject> >}; _Tp = libcamera::YamlObject::Value; allocator_type = std::allocator<libcamera::YamlObject::Value>]’\n> /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/vector.tcc:117:30:   required from ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<libcamera::YamlObject, std::default_delete<libcamera::YamlObject> >}; _Tp = libcamera::YamlObject::Value; _Alloc = std::allocator<libcamera::YamlObject::Value>; reference = libcamera::YamlObject::Value&]’\n> ../../src/libcamera/yaml_parser.cpp:491:27:   required from here\n> /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/new_allocator.h:191:11: error: cannot bind rvalue reference of type ‘std::string&&’ {aka ‘std::__cxx11::basic_string<char>&&’} to lvalue of type ‘const std::__cxx11::basic_string<char>’\n>    191 |         { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }\n>        |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n> ../../include/libcamera/internal/yaml_parser.h:31:37: note:   initializing argument 1 of ‘libcamera::YamlObject::Value::Value(std::string&&, std::unique_ptr<libcamera::YamlObject>&&)’\n>     31 |                 Value(std::string &&k, std::unique_ptr<YamlObject> &&v)\n>        |                       ~~~~~~~~~~~~~~^\n> \n> I'll drop the rvalue reference from the YamlObject::Value constructor.\n\nOops, admittedly I have not tested it. But then\n\n   list_.emplace_back(std::string(it->first), std::move(child)).value.get();\n\nshould work if it wants an rvalue reference.\n\n\n> \n>>>> +}\n>>>> +\n>>>>    #ifndef __DOXYGEN__\n>>>>\n>>>>    class YamlParserContext\n> \n> --\n> Regards,\n> \n> Laurent Pinchart","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 5188FC3220\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 19 Jan 2026 08:15:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 787D361FBC;\n\tMon, 19 Jan 2026 09:15:24 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2749C61F9F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 19 Jan 2026 09:15:23 +0100 (CET)","from [192.168.33.17] (185.221.143.114.nat.pool.zt.hu\n\t[185.221.143.114])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 754242D9;\n\tMon, 19 Jan 2026 09:14:52 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"nAIdktIW\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1768810492;\n\tbh=Qqp7zEqNCx+pSNEu/XUm9zz0zPTnujFyhjzXam9Rvbo=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=nAIdktIWIFxMOEW7HYFjfHA6DUUgHxvquOqlbGh3OHwX8H9YsFP91eAddIMdKY2B3\n\ta1usIR4Xj5g8hYsEJPmSyg6SMTDBvuV3QZWV7oj/qmhaSlKWbg7ehBxRxbTVSiyp76\n\t6j9zKqHkSiUseqQn8TSWILaxK7en02Mnxc5T5N0U=","Message-ID":"<17125812-6dce-4b08-badc-d51b11566fae@ideasonboard.com>","Date":"Mon, 19 Jan 2026 09:15:18 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>, =?utf-8?q?Barnab?=\n\t=?utf-8?b?w6FzIFDFkWN6ZQ==?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","References":"<20260113000808.15395-1-laurent.pinchart@ideasonboard.com>\n\t<uOU2iWgq9OsR65z0bhBmnJO1OJLZxB8hJMdi7gUaKdrtspGyw-pUZX959Od66Hc7otsMe7IPWXvUvRdnAOUMxQ==@protonmail.internalid>\n\t<20260113000808.15395-14-laurent.pinchart@ideasonboard.com>\n\t<a7bde25e-7b6e-4431-94c9-89b44bcc0602@ideasonboard.com>\n\t<20260113140355.GI6198@pendragon.ideasonboard.com>\n\t<EF26efuW9VUKfJY8VgrphL5HU4CuEaC48v_wa8n7aBUFQq0RgXMJwyZRNj7vyIKpM_MNTvtYxdIkkA8XElSKdQ==@protonmail.internalid>\n\t<20260119000728.GD7759@pendragon.ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260119000728.GD7759@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","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>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":37778,"web_url":"https://patchwork.libcamera.org/comment/37778/","msgid":"<20260120233907.GG173080@killaraus>","date":"2026-01-20T23:39:07","subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Mon, Jan 19, 2026 at 09:15:18AM +0100, Barnabás Pőcze wrote:\n> 2026. 01. 19. 1:07 keltezéssel, Laurent Pinchart írta:\n> > On Tue, Jan 13, 2026 at 04:03:55PM +0200, Laurent Pinchart wrote:\n> >> On Tue, Jan 13, 2026 at 10:43:18AM +0100, Barnabás Pőcze wrote:\n> >>> 2026. 01. 13. 1:07 keltezéssel, Laurent Pinchart írta:\n> >>>> Add YamlObject::add() functions to add children to a list or dictionary\n> >>>> object. This will be used by the YamlParserContext to replace direct\n> >>>> access to YamlObject member variables to decouple the two classes.\n> >>>>\n> >>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> >>>> ---\n> >>>>    include/libcamera/internal/yaml_parser.h |  3 ++\n> >>>>    src/libcamera/yaml_parser.cpp            | 56 ++++++++++++++++++++++++\n> >>>>    2 files changed, 59 insertions(+)\n> >>>>\n> >>>> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> >>>> index c98fe003c877..ede2c3de687c 100644\n> >>>> --- a/include/libcamera/internal/yaml_parser.h\n> >>>> +++ b/include/libcamera/internal/yaml_parser.h\n> >>>> @@ -196,6 +196,9 @@ public:\n> >>>>    \tbool contains(std::string_view key) const;\n> >>>>    \tconst YamlObject &operator[](std::string_view key) const;\n> >>>>\n> >>>> +\tYamlObject *add(std::unique_ptr<YamlObject> child);\n> >>>> +\tYamlObject *add(std::string key, std::unique_ptr<YamlObject> child);\n> >>>> +\n> >>>>    private:\n> >>>>    \tLIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)\n> >>>>\n> >>>> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> >>>> index 9b61d3e8fc50..7462e2d74179 100644\n> >>>> --- a/src/libcamera/yaml_parser.cpp\n> >>>> +++ b/src/libcamera/yaml_parser.cpp\n> >>>> @@ -448,6 +448,62 @@ const YamlObject &YamlObject::operator[](std::string_view key) const\n> >>>>    \treturn *iter->second;\n> >>>>    }\n> >>>>\n> >>>> +/**\n> >>>> + * \\brief Add a child object to a list\n> >>>> + * \\param[in] child The child object\n> >>>> + *\n> >>>> + * Append the \\a child node as the last element of this node's children list.\n> >>>> + * This node must be empty, in which case it is converted to the Type::List\n> >>>> + * type, or be a list. Otherwise, the \\a child is discarded and the function\n> >>>> + * returns a nullptr.\n> >>>> + *\n> >>>> + * \\return A pointer to the child object if successfully added, nullptr\n> >>>> + * otherwise\n> >>>> + */\n> >>>> +YamlObject *YamlObject::add(std::unique_ptr<YamlObject> child)\n> >>>> +{\n> >>>> +\tif (type_ == Type::Empty)\n> >>>> +\t\ttype_ = Type::List;\n> >>>> +\n> >>>> +\tif (type_ != Type::List)\n> >>>> +\t\treturn nullptr;\n> >>>> +\n> >>>> +\tValue &elem = list_.emplace_back(std::string{}, std::move(child));\n> >>>> +\treturn elem.value.get();\n> >>>> +}\n> >>>> +\n> >>>> +/**\n> >>>> + * \\brief Add a child object to a dictionary\n> >>>> + * \\param[in] key The dictionary key\n> >>>> + * \\param[in] child The child object\n> >>>> + *\n> >>>> + * Add the \\a child node with the given \\a key to this node's children. This\n> >>>> + * node must be empty, in which case it is converted to the Type::Dictionary\n> >>>> + * type, or be a dictionary. Otherwise, the \\a child is discarded and the\n> >>>> + * function returns a nullptr.\n> >>>> + *\n> >>>> + * Keys are unique. If a child with the same \\a key already exist, the \\a child\n> >>>> + * is discarded and the function returns a nullptr.\n> >>>\n> >>> I'm wondering if other behaviours, such as overwriting the child object make sense.\n> >>> Any thoughts? Or are these very specifically here to work with YamlParserContext?\n> >>\n> >> There should be no duplicate keys in YAML so both behaviours should work\n> >> fine for this use case. I don't know at this point which behaviour would\n> >> be the most sensible for the majority of use cases.\n> >>\n> >>>> + *\n> >>>> + * \\return A pointer to the child object if successfully added, nullptr\n> >>>> + * otherwise\n> >>>> + */\n> >>>> +YamlObject *YamlObject::add(std::string key, std::unique_ptr<YamlObject> child)\n> >>>> +{\n> >>>> +\tif (type_ == Type::Empty)\n> >>>> +\t\ttype_ = Type::Dictionary;\n> >>>> +\n> >>>> +\tif (type_ != Type::Dictionary)\n> >>>> +\t\treturn nullptr;\n> >>>> +\n> >>>> +\tif (dictionary_.find(key) != dictionary_.end())\n> >>>> +\t\treturn nullptr;\n> >>>> +\n> >>>> +\tValue &elem = list_.emplace_back(std::move(key), std::move(child));\n> >>>> +\tdictionary_.emplace(elem.key, elem.value.get());\n> >>>> +\treturn elem.value.get();\n> >>>\n> >>> Since `key` is already a string:\n> >>>\n> >>>     auto [it, inserted] = dictionary_.try_emplace(std::move(key), child.get());\n> >>>     if (!inserted)\n> >>>       return nullptr;\n> >>>\n> >>>     return list_.emplace_back(it->first, std::move(child)).value.get();\n> >>\n> >> I assume you meant that this would replace all code from\n> >> dictionary_.find() onwards. It seems fine, I'll do that in v2.\n> > \n> > In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/x86_64-pc-linux-gnu/bits/c++allocator.h:33,\n> >                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/allocator.h:46,\n> >                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/string:43,\n> >                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/locale_classes.h:40,\n> >                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/ios_base.h:41,\n> >                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/streambuf:43,\n> >                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/streambuf_iterator.h:35,\n> >                   from /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/iterator:66,\n> >                   from ../../include/libcamera/internal/yaml_parser.h:10,\n> >                   from ../../src/libcamera/yaml_parser.cpp:8:\n> > /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/new_allocator.h: In instantiation of ‘void std::__new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = libcamera::YamlObject::Value; _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<libcamera::YamlObject, std::default_delete<libcamera::YamlObject> >}; _Tp = libcamera::YamlObject::Value]’:\n> > /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/alloc_traits.h:538:17:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(allocator_type&, _Up*, _Args&& ...) [with _Up = libcamera::YamlObject::Value; _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<libcamera::YamlObject, std::default_delete<libcamera::YamlObject> >}; _Tp = libcamera::YamlObject::Value; allocator_type = std::allocator<libcamera::YamlObject::Value>]’\n> > /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/vector.tcc:117:30:   required from ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::unique_ptr<libcamera::YamlObject, std::default_delete<libcamera::YamlObject> >}; _Tp = libcamera::YamlObject::Value; _Alloc = std::allocator<libcamera::YamlObject::Value>; reference = libcamera::YamlObject::Value&]’\n> > ../../src/libcamera/yaml_parser.cpp:491:27:   required from here\n> > /usr/lib/gcc/x86_64-pc-linux-gnu/13/include/g++-v13/bits/new_allocator.h:191:11: error: cannot bind rvalue reference of type ‘std::string&&’ {aka ‘std::__cxx11::basic_string<char>&&’} to lvalue of type ‘const std::__cxx11::basic_string<char>’\n> >    191 |         { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }\n> >        |           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n> > ../../include/libcamera/internal/yaml_parser.h:31:37: note:   initializing argument 1 of ‘libcamera::YamlObject::Value::Value(std::string&&, std::unique_ptr<libcamera::YamlObject>&&)’\n> >     31 |                 Value(std::string &&k, std::unique_ptr<YamlObject> &&v)\n> >        |                       ~~~~~~~~~~~~~~^\n> > \n> > I'll drop the rvalue reference from the YamlObject::Value constructor.\n> \n> Oops, admittedly I have not tested it. But then\n> \n>    list_.emplace_back(std::string(it->first), std::move(child)).value.get();\n> \n> should work if it wants an rvalue reference.\n\nThat should work too, but dropping the rvalue reference to the\nconstructor works fine too for our use cases.\n\n> >>>> +}\n> >>>> +\n> >>>>    #ifndef __DOXYGEN__\n> >>>>\n> >>>>    class YamlParserContext","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 9FA79BDCBF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 20 Jan 2026 23:39:11 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C6B1761FC9;\n\tWed, 21 Jan 2026 00:39:10 +0100 (CET)","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 2EE5F606D5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 21 Jan 2026 00:39:09 +0100 (CET)","from pendragon.ideasonboard.com\n\t(2001-14ba-703d-e500--2a1.rev.dnainternet.fi\n\t[IPv6:2001:14ba:703d:e500::2a1])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 709A31E7;\n\tWed, 21 Jan 2026 00:38:37 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"SJq3ofPP\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1768952317;\n\tbh=mtxikt4kVowyO9KPgGkSYQdrW7bqxifNjp5sHICgrvg=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=SJq3ofPPyzrgQsk2WA3PDgKHieQ6O47bWa6GPFeHWLEzYmBDq8uIIwU+6EIF5Pbtt\n\taKAB/pIlSIiT6F6WGSenQfLs1y16EcpNGR2jvxRx7Pr3D+Z8FseIS8CAvMvne4J+cT\n\t+xSnWT8R1oQMwxNQs7b+sDKjwFOqmAW9mmec3L9Q=","Date":"Wed, 21 Jan 2026 01:39:07 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 13/36] libcamera: yaml_parser: Add functions to add\n\tchildren","Message-ID":"<20260120233907.GG173080@killaraus>","References":"<20260113000808.15395-1-laurent.pinchart@ideasonboard.com>\n\t<uOU2iWgq9OsR65z0bhBmnJO1OJLZxB8hJMdi7gUaKdrtspGyw-pUZX959Od66Hc7otsMe7IPWXvUvRdnAOUMxQ==@protonmail.internalid>\n\t<20260113000808.15395-14-laurent.pinchart@ideasonboard.com>\n\t<a7bde25e-7b6e-4431-94c9-89b44bcc0602@ideasonboard.com>\n\t<20260113140355.GI6198@pendragon.ideasonboard.com>\n\t<EF26efuW9VUKfJY8VgrphL5HU4CuEaC48v_wa8n7aBUFQq0RgXMJwyZRNj7vyIKpM_MNTvtYxdIkkA8XElSKdQ==@protonmail.internalid>\n\t<20260119000728.GD7759@pendragon.ideasonboard.com>\n\t<17125812-6dce-4b08-badc-d51b11566fae@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<17125812-6dce-4b08-badc-d51b11566fae@ideasonboard.com>","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>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]