Show a patch.

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

{
    "id": 25737,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/25737/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/25737/",
    "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": "<20260113000808.15395-18-laurent.pinchart@ideasonboard.com>",
    "date": "2026-01-13T00:07:49",
    "name": "[17/36] libcamera: yaml_parser: Split YamlObject from YamlParser",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "981a7dd31174c44120e4683bcada521657f98656",
    "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/25737/mbox/",
    "series": [
        {
            "id": 5703,
            "url": "https://patchwork.libcamera.org/api/1.1/series/5703/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5703",
            "date": "2026-01-13T00:07:32",
            "name": "libcamera: Global configuration file improvements",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/5703/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/25737/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/25737/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 DA3D5C32DE\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 13 Jan 2026 00:08:57 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6A17761FD7;\n\tTue, 13 Jan 2026 01:08:57 +0100 (CET)",
            "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2F83C61FC8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jan 2026 01:08:56 +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 49F4CF06\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 13 Jan 2026 01:08:30 +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=\"LMwBATiy\"; dkim-atps=neutral",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1768262910;\n\tbh=3/3XeS833dC0RKDMMaIuW6uxZHlGpT0KH70GyKfUqTw=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=LMwBATiy2UPzCZdIzZSQMDZE5eLzVQEc/ibhMeo9EkZHAELUWTv2W1jvsVZ307giW\n\tmMpmLYDAGYHE/sNwZLFPD1GDc7mpu1k93qy6UvfImCeEcKhJ9R2QdaCadOY5ckvLQg\n\tut5IPTJM8+pVr0e8Cbqp/84EZZuBUCyBgKZHaWf0=",
        "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Subject": "[PATCH 17/36] libcamera: yaml_parser: Split YamlObject from\n\tYamlParser",
        "Date": "Tue, 13 Jan 2026 02:07:49 +0200",
        "Message-ID": "<20260113000808.15395-18-laurent.pinchart@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.51.2",
        "In-Reply-To": "<20260113000808.15395-1-laurent.pinchart@ideasonboard.com>",
        "References": "<20260113000808.15395-1-laurent.pinchart@ideasonboard.com>",
        "MIME-Version": "1.0",
        "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>"
    },
    "content": "The YamlObject class was designed to represent data parsed from YAML\nfiles. It is outgrowing the initial needs. Move it to a separate file to\nprepare for a rename.\n\nMost files that include yaml_parser.h only need access to a YamlObject.\nSwitch them to yaml_object.h. pipeline_base.cpp was including\nyaml_parser.h indirectly through pipeline_base.h, include it directly\nnow.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n .../libcamera/internal/global_configuration.h |   2 +-\n include/libcamera/internal/matrix.h           |   2 +-\n include/libcamera/internal/meson.build        |   1 +\n include/libcamera/internal/vector.h           |   2 +-\n include/libcamera/internal/yaml_object.h      | 226 +++++++++\n include/libcamera/internal/yaml_parser.h      | 212 +-------\n src/ipa/libipa/agc_mean_luminance.h           |   2 +-\n src/ipa/libipa/awb.h                          |   2 +-\n src/ipa/libipa/awb_bayes.h                    |   2 +-\n src/ipa/libipa/interpolator.cpp               |   2 -\n src/ipa/libipa/interpolator.h                 |   2 +-\n src/ipa/libipa/lsc_polynomial.h               |   2 +-\n src/ipa/libipa/lux.cpp                        |   2 +-\n src/ipa/libipa/module.h                       |   2 +-\n src/ipa/mali-c55/algorithms/blc.cpp           |   2 +-\n src/ipa/mali-c55/algorithms/lsc.cpp           |   2 +-\n src/ipa/rkisp1/algorithms/agc.cpp             |   2 +-\n src/ipa/rkisp1/algorithms/blc.cpp             |   2 +-\n src/ipa/rkisp1/algorithms/ccm.cpp             |   2 +-\n src/ipa/rkisp1/algorithms/dpcc.cpp            |   2 +-\n src/ipa/rkisp1/algorithms/goc.cpp             |   2 +-\n src/ipa/rkisp1/algorithms/gsl.cpp             |   2 +-\n src/ipa/rkisp1/algorithms/lsc.cpp             |   2 +-\n src/ipa/rkisp1/algorithms/wdr.cpp             |   2 +-\n src/ipa/rpi/controller/algorithm.h            |   2 +-\n src/ipa/rpi/controller/controller.h           |   2 +-\n src/libcamera/geometry.cpp                    |   2 +-\n src/libcamera/meson.build                     |   1 +\n .../pipeline/rpi/common/pipeline_base.cpp     |   1 +\n .../pipeline/rpi/common/pipeline_base.h       |   2 +-\n .../pipeline/virtual/config_parser.h          |   2 +-\n src/libcamera/pipeline/virtual/virtual.cpp    |   2 +-\n src/libcamera/yaml_object.cpp                 | 477 ++++++++++++++++++\n src/libcamera/yaml_parser.cpp                 | 458 +----------------\n 34 files changed, 736 insertions(+), 694 deletions(-)\n create mode 100644 include/libcamera/internal/yaml_object.h\n create mode 100644 src/libcamera/yaml_object.cpp",
    "diff": "diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h\nindex 8d09517edca1..16c6a21f2a8a 100644\n--- a/include/libcamera/internal/global_configuration.h\n+++ b/include/libcamera/internal/global_configuration.h\n@@ -14,7 +14,7 @@\n \n #include <libcamera/base/utils.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n namespace libcamera {\n \ndiff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h\nindex 67761cb6037d..11fccd27547e 100644\n--- a/include/libcamera/internal/matrix.h\n+++ b/include/libcamera/internal/matrix.h\n@@ -14,7 +14,7 @@\n #include <libcamera/base/log.h>\n #include <libcamera/base/span.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n namespace libcamera {\n \ndiff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build\nindex dc48619e5ac8..c24c9f062f29 100644\n--- a/include/libcamera/internal/meson.build\n+++ b/include/libcamera/internal/meson.build\n@@ -50,6 +50,7 @@ libcamera_internal_headers = files([\n     'v4l2_subdevice.h',\n     'v4l2_videodevice.h',\n     'vector.h',\n+    'yaml_object.h',\n     'yaml_parser.h',\n ])\n \ndiff --git a/include/libcamera/internal/vector.h b/include/libcamera/internal/vector.h\nindex cfd8882ce0e6..af24485f3bb1 100644\n--- a/include/libcamera/internal/vector.h\n+++ b/include/libcamera/internal/vector.h\n@@ -19,7 +19,7 @@\n #include <libcamera/base/span.h>\n \n #include \"libcamera/internal/matrix.h\"\n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n namespace libcamera {\n \ndiff --git a/include/libcamera/internal/yaml_object.h b/include/libcamera/internal/yaml_object.h\nnew file mode 100644\nindex 000000000000..1771f8821e2e\n--- /dev/null\n+++ b/include/libcamera/internal/yaml_object.h\n@@ -0,0 +1,226 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ * Copyright (C) 2025, Ideas on Board\n+ *\n+ * libcamera YAML object\n+ */\n+\n+#pragma once\n+\n+#include <iterator>\n+#include <map>\n+#include <memory>\n+#include <optional>\n+#include <string>\n+#include <string_view>\n+#include <type_traits>\n+#include <utility>\n+#include <vector>\n+\n+#include <libcamera/base/class.h>\n+\n+namespace libcamera {\n+\n+class YamlObject\n+{\n+private:\n+\tstruct Value {\n+\t\tValue(std::string &&k, std::unique_ptr<YamlObject> &&v)\n+\t\t\t: key(std::move(k)), value(std::move(v))\n+\t\t{\n+\t\t}\n+\t\tstd::string key;\n+\t\tstd::unique_ptr<YamlObject> value;\n+\t};\n+\n+\tusing ValueContainer = std::vector<Value>;\n+\n+public:\n+#ifndef __DOXYGEN__\n+\ttemplate<typename Derived>\n+\tclass Iterator\n+\t{\n+\tpublic:\n+\t\tusing difference_type = std::ptrdiff_t;\n+\t\tusing iterator_category = std::forward_iterator_tag;\n+\n+\t\tIterator(typename ValueContainer::const_iterator it)\n+\t\t\t: it_(it)\n+\t\t{\n+\t\t}\n+\n+\t\tDerived &operator++()\n+\t\t{\n+\t\t\t++it_;\n+\t\t\treturn *static_cast<Derived *>(this);\n+\t\t}\n+\n+\t\tDerived operator++(int)\n+\t\t{\n+\t\t\tDerived it = *static_cast<Derived *>(this);\n+\t\t\tit_++;\n+\t\t\treturn it;\n+\t\t}\n+\n+\t\tfriend bool operator==(const Iterator &a, const Iterator &b)\n+\t\t{\n+\t\t\treturn a.it_ == b.it_;\n+\t\t}\n+\n+\t\tfriend bool operator!=(const Iterator &a, const Iterator &b)\n+\t\t{\n+\t\t\treturn a.it_ != b.it_;\n+\t\t}\n+\n+\tprotected:\n+\t\tValueContainer::const_iterator it_;\n+\t};\n+\n+\ttemplate<typename Iterator>\n+\tclass Adapter\n+\t{\n+\tpublic:\n+\t\tAdapter(const ValueContainer &container)\n+\t\t\t: container_(container)\n+\t\t{\n+\t\t}\n+\n+\t\tIterator begin() const\n+\t\t{\n+\t\t\treturn Iterator{ container_.begin() };\n+\t\t}\n+\n+\t\tIterator end() const\n+\t\t{\n+\t\t\treturn Iterator{ container_.end() };\n+\t\t}\n+\n+\tprotected:\n+\t\tconst ValueContainer &container_;\n+\t};\n+\n+\tclass ListIterator : public Iterator<ListIterator>\n+\t{\n+\tpublic:\n+\t\tusing value_type = const YamlObject &;\n+\t\tusing pointer = const YamlObject *;\n+\t\tusing reference = value_type;\n+\n+\t\tvalue_type operator*() const\n+\t\t{\n+\t\t\treturn *it_->value.get();\n+\t\t}\n+\n+\t\tpointer operator->() const\n+\t\t{\n+\t\t\treturn it_->value.get();\n+\t\t}\n+\t};\n+\n+\tclass DictIterator : public Iterator<DictIterator>\n+\t{\n+\tpublic:\n+\t\tusing value_type = std::pair<const std::string &, const YamlObject &>;\n+\t\tusing pointer = value_type *;\n+\t\tusing reference = value_type &;\n+\n+\t\tvalue_type operator*() const\n+\t\t{\n+\t\t\treturn { it_->key, *it_->value.get() };\n+\t\t}\n+\t};\n+\n+\tclass DictAdapter : public Adapter<DictIterator>\n+\t{\n+\tpublic:\n+\t\tusing key_type = std::string;\n+\t};\n+\n+\tclass ListAdapter : public Adapter<ListIterator>\n+\t{\n+\t};\n+#endif /* __DOXYGEN__ */\n+\n+\tYamlObject();\n+\t~YamlObject();\n+\n+\tbool isValue() const\n+\t{\n+\t\treturn type_ == Type::Value;\n+\t}\n+\tbool isList() const\n+\t{\n+\t\treturn type_ == Type::List;\n+\t}\n+\tbool isDictionary() const\n+\t{\n+\t\treturn type_ == Type::Dictionary;\n+\t}\n+\tbool isEmpty() const\n+\t{\n+\t\treturn type_ == Type::Empty;\n+\t}\n+\texplicit operator bool() const\n+\t{\n+\t\treturn type_ != Type::Empty;\n+\t}\n+\n+\tstd::size_t size() const;\n+\n+\ttemplate<typename T>\n+\tstd::optional<T> get() const\n+\t{\n+\t\treturn Accessor<T>{}.get(*this);\n+\t}\n+\n+\ttemplate<typename T, typename U>\n+\tT get(U &&defaultValue) const\n+\t{\n+\t\treturn get<T>().value_or(std::forward<U>(defaultValue));\n+\t}\n+\n+\ttemplate<typename T>\n+\tvoid set(T &&value)\n+\t{\n+\t\treturn Accessor<std::remove_reference_t<T>>{}.set(*this, std::forward<T>(value));\n+\t}\n+\n+\tDictAdapter asDict() const { return DictAdapter{ list_ }; }\n+\tListAdapter asList() const { return ListAdapter{ list_ }; }\n+\n+\tconst YamlObject &operator[](std::size_t index) const;\n+\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+\ttemplate<typename T>\n+\tfriend struct Accessor;\n+\n+\tenum class Type {\n+\t\tDictionary,\n+\t\tList,\n+\t\tValue,\n+\t\tEmpty,\n+\t};\n+\n+\ttemplate<typename T, typename Enable = void>\n+\tstruct Accessor {\n+\t\tstd::optional<T> get(const YamlObject &obj) const;\n+\t\tvoid set(YamlObject &obj, T value);\n+\t};\n+\n+\tType type_;\n+\n+\tstd::string value_;\n+\tValueContainer list_;\n+\tstd::map<std::string, YamlObject *, std::less<>> dictionary_;\n+};\n+\n+} /* namespace libcamera */\ndiff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\nindex 7b6d16de1a04..e503e83a80da 100644\n--- a/include/libcamera/internal/yaml_parser.h\n+++ b/include/libcamera/internal/yaml_parser.h\n@@ -7,221 +7,13 @@\n \n #pragma once\n \n-#include <iterator>\n-#include <map>\n-#include <optional>\n-#include <stdint.h>\n-#include <string>\n-#include <string_view>\n-#include <vector>\n+#include <memory>\n \n-#include <libcamera/base/class.h>\n+#include \"libcamera/internal/yaml_object.h\"\n \n namespace libcamera {\n \n class File;\n-class YamlParserContext;\n-\n-class YamlObject\n-{\n-private:\n-\tstruct Value {\n-\t\tValue(std::string &&k, std::unique_ptr<YamlObject> &&v)\n-\t\t\t: key(std::move(k)), value(std::move(v))\n-\t\t{\n-\t\t}\n-\t\tstd::string key;\n-\t\tstd::unique_ptr<YamlObject> value;\n-\t};\n-\n-\tusing ValueContainer = std::vector<Value>;\n-\n-public:\n-#ifndef __DOXYGEN__\n-\ttemplate<typename Derived>\n-\tclass Iterator\n-\t{\n-\tpublic:\n-\t\tusing difference_type = std::ptrdiff_t;\n-\t\tusing iterator_category = std::forward_iterator_tag;\n-\n-\t\tIterator(typename ValueContainer::const_iterator it)\n-\t\t\t: it_(it)\n-\t\t{\n-\t\t}\n-\n-\t\tDerived &operator++()\n-\t\t{\n-\t\t\t++it_;\n-\t\t\treturn *static_cast<Derived *>(this);\n-\t\t}\n-\n-\t\tDerived operator++(int)\n-\t\t{\n-\t\t\tDerived it = *static_cast<Derived *>(this);\n-\t\t\tit_++;\n-\t\t\treturn it;\n-\t\t}\n-\n-\t\tfriend bool operator==(const Iterator &a, const Iterator &b)\n-\t\t{\n-\t\t\treturn a.it_ == b.it_;\n-\t\t}\n-\n-\t\tfriend bool operator!=(const Iterator &a, const Iterator &b)\n-\t\t{\n-\t\t\treturn a.it_ != b.it_;\n-\t\t}\n-\n-\tprotected:\n-\t\tValueContainer::const_iterator it_;\n-\t};\n-\n-\ttemplate<typename Iterator>\n-\tclass Adapter\n-\t{\n-\tpublic:\n-\t\tAdapter(const ValueContainer &container)\n-\t\t\t: container_(container)\n-\t\t{\n-\t\t}\n-\n-\t\tIterator begin() const\n-\t\t{\n-\t\t\treturn Iterator{ container_.begin() };\n-\t\t}\n-\n-\t\tIterator end() const\n-\t\t{\n-\t\t\treturn Iterator{ container_.end() };\n-\t\t}\n-\n-\tprotected:\n-\t\tconst ValueContainer &container_;\n-\t};\n-\n-\tclass ListIterator : public Iterator<ListIterator>\n-\t{\n-\tpublic:\n-\t\tusing value_type = const YamlObject &;\n-\t\tusing pointer = const YamlObject *;\n-\t\tusing reference = value_type;\n-\n-\t\tvalue_type operator*() const\n-\t\t{\n-\t\t\treturn *it_->value.get();\n-\t\t}\n-\n-\t\tpointer operator->() const\n-\t\t{\n-\t\t\treturn it_->value.get();\n-\t\t}\n-\t};\n-\n-\tclass DictIterator : public Iterator<DictIterator>\n-\t{\n-\tpublic:\n-\t\tusing value_type = std::pair<const std::string &, const YamlObject &>;\n-\t\tusing pointer = value_type *;\n-\t\tusing reference = value_type &;\n-\n-\t\tvalue_type operator*() const\n-\t\t{\n-\t\t\treturn { it_->key, *it_->value.get() };\n-\t\t}\n-\t};\n-\n-\tclass DictAdapter : public Adapter<DictIterator>\n-\t{\n-\tpublic:\n-\t\tusing key_type = std::string;\n-\t};\n-\n-\tclass ListAdapter : public Adapter<ListIterator>\n-\t{\n-\t};\n-#endif /* __DOXYGEN__ */\n-\n-\tYamlObject();\n-\t~YamlObject();\n-\n-\tbool isValue() const\n-\t{\n-\t\treturn type_ == Type::Value;\n-\t}\n-\tbool isList() const\n-\t{\n-\t\treturn type_ == Type::List;\n-\t}\n-\tbool isDictionary() const\n-\t{\n-\t\treturn type_ == Type::Dictionary;\n-\t}\n-\tbool isEmpty() const\n-\t{\n-\t\treturn type_ == Type::Empty;\n-\t}\n-\texplicit operator bool() const\n-\t{\n-\t\treturn type_ != Type::Empty;\n-\t}\n-\n-\tstd::size_t size() const;\n-\n-\ttemplate<typename T>\n-\tstd::optional<T> get() const\n-\t{\n-\t\treturn Accessor<T>{}.get(*this);\n-\t}\n-\n-\ttemplate<typename T, typename U>\n-\tT get(U &&defaultValue) const\n-\t{\n-\t\treturn get<T>().value_or(std::forward<U>(defaultValue));\n-\t}\n-\n-\ttemplate<typename T>\n-\tvoid set(T &&value)\n-\t{\n-\t\treturn Accessor<std::remove_reference_t<T>>{}.set(*this, std::forward<T>(value));\n-\t}\n-\n-\tDictAdapter asDict() const { return DictAdapter{ list_ }; }\n-\tListAdapter asList() const { return ListAdapter{ list_ }; }\n-\n-\tconst YamlObject &operator[](std::size_t index) const;\n-\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-\ttemplate<typename T>\n-\tfriend struct Accessor;\n-\n-\tenum class Type {\n-\t\tDictionary,\n-\t\tList,\n-\t\tValue,\n-\t\tEmpty,\n-\t};\n-\n-\ttemplate<typename T, typename Enable = void>\n-\tstruct Accessor {\n-\t\tstd::optional<T> get(const YamlObject &obj) const;\n-\t\tvoid set(YamlObject &obj, T value);\n-\t};\n-\n-\tType type_;\n-\n-\tstd::string value_;\n-\tValueContainer list_;\n-\tstd::map<std::string, YamlObject *, std::less<>> dictionary_;\n-};\n \n class YamlParser final\n {\ndiff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h\nindex 0df97b27332d..dfe1ccbe948b 100644\n--- a/src/ipa/libipa/agc_mean_luminance.h\n+++ b/src/ipa/libipa/agc_mean_luminance.h\n@@ -16,7 +16,7 @@\n \n #include <libcamera/controls.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"exposure_mode_helper.h\"\n #include \"histogram.h\"\ndiff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h\nindex f4a86038635f..3f25d13feaa8 100644\n--- a/src/ipa/libipa/awb.h\n+++ b/src/ipa/libipa/awb.h\n@@ -14,7 +14,7 @@\n #include <libcamera/controls.h>\n \n #include \"libcamera/internal/vector.h\"\n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n namespace libcamera {\n \ndiff --git a/src/ipa/libipa/awb_bayes.h b/src/ipa/libipa/awb_bayes.h\nindex 47ef3cce4d58..79334ad3e7a3 100644\n--- a/src/ipa/libipa/awb_bayes.h\n+++ b/src/ipa/libipa/awb_bayes.h\n@@ -10,7 +10,7 @@\n #include <libcamera/controls.h>\n \n #include \"libcamera/internal/vector.h\"\n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"awb.h\"\n #include \"interpolator.h\"\ndiff --git a/src/ipa/libipa/interpolator.cpp b/src/ipa/libipa/interpolator.cpp\nindex f901a86e4c74..9355802f796a 100644\n--- a/src/ipa/libipa/interpolator.cpp\n+++ b/src/ipa/libipa/interpolator.cpp\n@@ -11,8 +11,6 @@\n \n #include <libcamera/base/log.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n-\n #include \"interpolator.h\"\n \n /**\ndiff --git a/src/ipa/libipa/interpolator.h b/src/ipa/libipa/interpolator.h\nindex 7880db6976d1..08003a06dbba 100644\n--- a/src/ipa/libipa/interpolator.h\n+++ b/src/ipa/libipa/interpolator.h\n@@ -15,7 +15,7 @@\n \n #include <libcamera/base/log.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n namespace libcamera {\n \ndiff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h\nindex c71f215de3fc..19da46860849 100644\n--- a/src/ipa/libipa/lsc_polynomial.h\n+++ b/src/ipa/libipa/lsc_polynomial.h\n@@ -16,7 +16,7 @@\n \n #include <libcamera/geometry.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n namespace libcamera {\n \ndiff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp\nindex 899e88248f04..d79b116a3339 100644\n--- a/src/ipa/libipa/lux.cpp\n+++ b/src/ipa/libipa/lux.cpp\n@@ -12,7 +12,7 @@\n \n #include <libcamera/base/log.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"histogram.h\"\n \ndiff --git a/src/ipa/libipa/module.h b/src/ipa/libipa/module.h\nindex c27af7718feb..8e6c658a6b63 100644\n--- a/src/ipa/libipa/module.h\n+++ b/src/ipa/libipa/module.h\n@@ -15,7 +15,7 @@\n #include <libcamera/base/log.h>\n #include <libcamera/base/utils.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"algorithm.h\"\n \ndiff --git a/src/ipa/mali-c55/algorithms/blc.cpp b/src/ipa/mali-c55/algorithms/blc.cpp\nindex d099219c3e43..3bdf19141e1d 100644\n--- a/src/ipa/mali-c55/algorithms/blc.cpp\n+++ b/src/ipa/mali-c55/algorithms/blc.cpp\n@@ -10,7 +10,7 @@\n #include <libcamera/base/log.h>\n #include <libcamera/control_ids.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n /**\n  * \\file blc.h\ndiff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp\nindex f75b9cd7b7b8..e32f5a83485e 100644\n--- a/src/ipa/mali-c55/algorithms/lsc.cpp\n+++ b/src/ipa/mali-c55/algorithms/lsc.cpp\n@@ -7,7 +7,7 @@\n \n #include \"lsc.h\"\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n namespace libcamera {\n \ndiff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp\nindex 7cc06f91ac2b..ef16a3775056 100644\n--- a/src/ipa/rkisp1/algorithms/agc.cpp\n+++ b/src/ipa/rkisp1/algorithms/agc.cpp\n@@ -19,7 +19,7 @@\n #include <libcamera/control_ids.h>\n #include <libcamera/ipa/core_ipa_interface.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"libipa/histogram.h\"\n \ndiff --git a/src/ipa/rkisp1/algorithms/blc.cpp b/src/ipa/rkisp1/algorithms/blc.cpp\nindex 32fc44ffff92..19c262fffa73 100644\n--- a/src/ipa/rkisp1/algorithms/blc.cpp\n+++ b/src/ipa/rkisp1/algorithms/blc.cpp\n@@ -13,7 +13,7 @@\n \n #include <libcamera/control_ids.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n /**\n  * \\file blc.h\ndiff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp\nindex de2b6fe775aa..3ed307280677 100644\n--- a/src/ipa/rkisp1/algorithms/ccm.cpp\n+++ b/src/ipa/rkisp1/algorithms/ccm.cpp\n@@ -16,7 +16,7 @@\n \n #include <libcamera/ipa/core_ipa_interface.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"libipa/fixedpoint.h\"\n #include \"libipa/interpolator.h\"\ndiff --git a/src/ipa/rkisp1/algorithms/dpcc.cpp b/src/ipa/rkisp1/algorithms/dpcc.cpp\nindex 7894628144f3..c195334750e1 100644\n--- a/src/ipa/rkisp1/algorithms/dpcc.cpp\n+++ b/src/ipa/rkisp1/algorithms/dpcc.cpp\n@@ -9,7 +9,7 @@\n \n #include <libcamera/base/log.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"linux/rkisp1-config.h\"\n \ndiff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp\nindex a0e7030fe5db..6c07bd8c71e5 100644\n--- a/src/ipa/rkisp1/algorithms/goc.cpp\n+++ b/src/ipa/rkisp1/algorithms/goc.cpp\n@@ -13,7 +13,7 @@\n \n #include <libcamera/control_ids.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"linux/rkisp1-config.h\"\n \ndiff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp\nindex 7ac5dc215850..292d0e80dc57 100644\n--- a/src/ipa/rkisp1/algorithms/gsl.cpp\n+++ b/src/ipa/rkisp1/algorithms/gsl.cpp\n@@ -10,7 +10,7 @@\n #include <libcamera/base/log.h>\n #include <libcamera/base/utils.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"linux/rkisp1-config.h\"\n \ndiff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp\nindex 429565a0b51f..9c0ed44b3943 100644\n--- a/src/ipa/rkisp1/algorithms/lsc.cpp\n+++ b/src/ipa/rkisp1/algorithms/lsc.cpp\n@@ -14,7 +14,7 @@\n #include <libcamera/base/log.h>\n #include <libcamera/base/utils.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"libipa/lsc_polynomial.h\"\n #include \"linux/rkisp1-config.h\"\ndiff --git a/src/ipa/rkisp1/algorithms/wdr.cpp b/src/ipa/rkisp1/algorithms/wdr.cpp\nindex ed81628c032c..9e2688a05179 100644\n--- a/src/ipa/rkisp1/algorithms/wdr.cpp\n+++ b/src/ipa/rkisp1/algorithms/wdr.cpp\n@@ -10,7 +10,7 @@\n #include <libcamera/base/log.h>\n #include <libcamera/base/utils.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include <libipa/agc_mean_luminance.h>\n #include <libipa/histogram.h>\ndiff --git a/src/ipa/rpi/controller/algorithm.h b/src/ipa/rpi/controller/algorithm.h\nindex 1971bfdcc8ad..8839daa90916 100644\n--- a/src/ipa/rpi/controller/algorithm.h\n+++ b/src/ipa/rpi/controller/algorithm.h\n@@ -15,7 +15,7 @@\n #include <memory>\n #include <map>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"controller.h\"\n \ndiff --git a/src/ipa/rpi/controller/controller.h b/src/ipa/rpi/controller/controller.h\nindex fdb46557de9c..573942886bc0 100644\n--- a/src/ipa/rpi/controller/controller.h\n+++ b/src/ipa/rpi/controller/controller.h\n@@ -16,7 +16,7 @@\n #include <string>\n \n #include <libcamera/base/utils.h>\n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"camera_mode.h\"\n #include \"device_status.h\"\ndiff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp\nindex 2e8e9e33ea78..621008844c74 100644\n--- a/src/libcamera/geometry.cpp\n+++ b/src/libcamera/geometry.cpp\n@@ -12,7 +12,7 @@\n \n #include <libcamera/base/log.h>\n \n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n /**\n  * \\file geometry.h\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex 6f952bd9832a..da89aa3714c3 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -58,6 +58,7 @@ libcamera_internal_sources = files([\n     'v4l2_subdevice.cpp',\n     'v4l2_videodevice.cpp',\n     'vector.cpp',\n+    'yaml_object.cpp',\n     'yaml_parser.cpp',\n ])\n \ndiff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\nindex 7eb197ce435c..684438bd5fb7 100644\n--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n@@ -21,6 +21,7 @@\n \n #include \"libcamera/internal/camera_lens.h\"\n #include \"libcamera/internal/v4l2_subdevice.h\"\n+#include \"libcamera/internal/yaml_parser.h\"\n \n using namespace std::chrono_literals;\n \ndiff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h\nindex 6257a93467df..c69a690f580c 100644\n--- a/src/libcamera/pipeline/rpi/common/pipeline_base.h\n+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h\n@@ -26,7 +26,7 @@\n #include \"libcamera/internal/pipeline_handler.h\"\n #include \"libcamera/internal/request.h\"\n #include \"libcamera/internal/v4l2_videodevice.h\"\n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include <libcamera/ipa/raspberrypi_ipa_interface.h>\n #include <libcamera/ipa/raspberrypi_ipa_proxy.h>\ndiff --git a/src/libcamera/pipeline/virtual/config_parser.h b/src/libcamera/pipeline/virtual/config_parser.h\nindex d2000de9c12f..f696d8862897 100644\n--- a/src/libcamera/pipeline/virtual/config_parser.h\n+++ b/src/libcamera/pipeline/virtual/config_parser.h\n@@ -13,7 +13,7 @@\n #include <libcamera/base/file.h>\n \n #include \"libcamera/internal/pipeline_handler.h\"\n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"virtual.h\"\n \ndiff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp\nindex 40c35264c568..b032db54ded4 100644\n--- a/src/libcamera/pipeline/virtual/virtual.cpp\n+++ b/src/libcamera/pipeline/virtual/virtual.cpp\n@@ -36,7 +36,7 @@\n #include \"libcamera/internal/framebuffer.h\"\n #include \"libcamera/internal/pipeline_handler.h\"\n #include \"libcamera/internal/request.h\"\n-#include \"libcamera/internal/yaml_parser.h\"\n+#include \"libcamera/internal/yaml_object.h\"\n \n #include \"pipeline/virtual/config_parser.h\"\n \ndiff --git a/src/libcamera/yaml_object.cpp b/src/libcamera/yaml_object.cpp\nnew file mode 100644\nindex 000000000000..5e92189fdb8e\n--- /dev/null\n+++ b/src/libcamera/yaml_object.cpp\n@@ -0,0 +1,477 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2022, Google Inc.\n+ * Copyright (C) 2025, Ideas on Board.\n+ *\n+ * libcamera YAML object\n+ */\n+\n+#include \"libcamera/internal/yaml_object.h\"\n+\n+#include <charconv>\n+#include <errno.h>\n+#include <string>\n+#include <vector>\n+\n+#include <libcamera/base/utils.h>\n+\n+/**\n+ * \\file yaml_object.h\n+ * \\brief YAML objects\n+ */\n+\n+namespace libcamera {\n+\n+namespace {\n+\n+/* Empty static YamlObject as a safe result for invalid operations */\n+static const YamlObject empty;\n+\n+} /* namespace */\n+\n+/**\n+ * \\class YamlObject\n+ * \\brief A class representing the tree structure of the YAML content\n+ *\n+ * The YamlObject class represents the tree structure of YAML content. A\n+ * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a\n+ * tree leaf.\n+ */\n+\n+YamlObject::YamlObject()\n+\t: type_(Type::Empty)\n+{\n+}\n+\n+YamlObject::~YamlObject() = default;\n+\n+/**\n+ * \\fn YamlObject::isValue()\n+ * \\brief Return whether the YamlObject is a value\n+ *\n+ * \\return True if the YamlObject is a value, false otherwise\n+ */\n+\n+/**\n+ * \\fn YamlObject::isList()\n+ * \\brief Return whether the YamlObject is a list\n+ *\n+ * \\return True if the YamlObject is a list, false otherwise\n+ */\n+\n+/**\n+ * \\fn YamlObject::isDictionary()\n+ * \\brief Return whether the YamlObject is a dictionary\n+ *\n+ * \\return True if the YamlObject is a dictionary, false otherwise\n+ */\n+\n+/**\n+ * \\fn YamlObject::isEmpty()\n+ * \\brief Return whether the YamlObject is an empty\n+ *\n+ * \\return True if the YamlObject is empty, false otherwise\n+ */\n+\n+/**\n+ * \\fn YamlObject::operator bool()\n+ * \\brief Return whether the YamlObject is a non-empty\n+ *\n+ * \\return False if the YamlObject is empty, true otherwise\n+ */\n+\n+/**\n+ * \\fn YamlObject::size()\n+ * \\brief Retrieve the number of elements in a dictionary or list YamlObject\n+ *\n+ * This function retrieves the size of the YamlObject, defined as the number of\n+ * child elements it contains. Only YamlObject instances of Dictionary or List\n+ * types have a size, calling this function on other types of instances is\n+ * invalid and results in undefined behaviour.\n+ *\n+ * \\return The size of the YamlObject\n+ */\n+std::size_t YamlObject::size() const\n+{\n+\tswitch (type_) {\n+\tcase Type::Dictionary:\n+\tcase Type::List:\n+\t\treturn list_.size();\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n+/**\n+ * \\fn template<typename T> YamlObject::get<T>() const\n+ * \\tparam T Type of the value\n+ * \\brief Parse the YamlObject as a \\a T value\n+ *\n+ * This function parses the value of the YamlObject as a \\a T object, and\n+ * returns the value. If parsing fails (usually because the YamlObject doesn't\n+ * store a \\a T value), std::nullopt is returned.\n+ *\n+ * If the type \\a T is an std::vector, the YamlObject will be parsed as a list\n+ * of values.\n+ *\n+ * \\return The YamlObject value, or std::nullopt if parsing failed\n+ */\n+\n+/**\n+ * \\fn template<typename T, typename U> YamlObject::get<T>(U &&defaultValue) const\n+ * \\brief Parse the YamlObject as a \\a T value\n+ * \\param[in] defaultValue The default value when failing to parse\n+ *\n+ * This function parses the value of the YamlObject as a \\a T object, and\n+ * returns the value. If parsing fails (usually because the YamlObject doesn't\n+ * store a \\a T value), the \\a defaultValue is returned.\n+ *\n+ * Unlike the get() function, this overload does not support std::vector for the\n+ * type \\a T.\n+ *\n+ * \\return The YamlObject value, or \\a defaultValue if parsing failed\n+ */\n+\n+/**\n+ * \\fn template<typename T> YamlObject::set<T>(T &&value)\n+ * \\brief Set the value of a YamlObject\n+ * \\param[in] value The value\n+ *\n+ * This function sets the value stored in a YamlObject to \\a value. The value is\n+ * converted to a string in an implementation-specific way that guarantees that\n+ * subsequent calls to get<T>() will return the same value.\n+ *\n+ * Values can only be set on YamlObject of Type::Value type or empty YamlObject.\n+ * Attempting to set a value on an object of type Type::Dict or Type::List does\n+ * not modify the YamlObject.\n+ */\n+\n+#ifndef __DOXYGEN__\n+\n+template<>\n+std::optional<bool>\n+YamlObject::Accessor<bool>::get(const YamlObject &obj) const\n+{\n+\tif (obj.type_ != Type::Value)\n+\t\treturn std::nullopt;\n+\n+\tif (obj.value_ == \"true\")\n+\t\treturn true;\n+\telse if (obj.value_ == \"false\")\n+\t\treturn false;\n+\n+\treturn std::nullopt;\n+}\n+\n+template<>\n+void YamlObject::Accessor<bool>::set(YamlObject &obj, bool value)\n+{\n+\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n+\t\treturn;\n+\n+\tobj.type_ = Type::Value;\n+\tobj.value_ = value ? \"true\" : \"false\";\n+}\n+\n+template<typename T>\n+struct YamlObject::Accessor<T, std::enable_if_t<\n+\tstd::is_same_v<int8_t, T> ||\n+\tstd::is_same_v<uint8_t, T> ||\n+\tstd::is_same_v<int16_t, T> ||\n+\tstd::is_same_v<uint16_t, T> ||\n+\tstd::is_same_v<int32_t, T> ||\n+\tstd::is_same_v<uint32_t, T>>>\n+{\n+\tstd::optional<T> get(const YamlObject &obj) const\n+\t{\n+\t\tif (obj.type_ != Type::Value)\n+\t\t\treturn std::nullopt;\n+\n+\t\tconst std::string &str = obj.value_;\n+\t\tT value;\n+\n+\t\tauto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(),\n+\t\t\t\t\t\t value);\n+\t\tif (ptr != str.data() + str.size() || ec != std::errc())\n+\t\t\treturn std::nullopt;\n+\n+\t\treturn value;\n+\t}\n+\n+\tvoid set(YamlObject &obj, T value)\n+\t{\n+\t\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n+\t\t\treturn;\n+\n+\t\tobj.type_ = Type::Value;\n+\t\tobj.value_ = std::to_string(value);\n+\t}\n+};\n+\n+template struct YamlObject::Accessor<int8_t>;\n+template struct YamlObject::Accessor<uint8_t>;\n+template struct YamlObject::Accessor<int16_t>;\n+template struct YamlObject::Accessor<uint16_t>;\n+template struct YamlObject::Accessor<int32_t>;\n+template struct YamlObject::Accessor<uint32_t>;\n+\n+template<>\n+std::optional<float>\n+YamlObject::Accessor<float>::get(const YamlObject &obj) const\n+{\n+\treturn obj.get<double>();\n+}\n+\n+template<>\n+void YamlObject::Accessor<float>::set(YamlObject &obj, float value)\n+{\n+\tobj.set<double>(std::forward<float>(value));\n+}\n+\n+template<>\n+std::optional<double>\n+YamlObject::Accessor<double>::get(const YamlObject &obj) const\n+{\n+\tif (obj.type_ != Type::Value)\n+\t\treturn std::nullopt;\n+\n+\tif (obj.value_.empty())\n+\t\treturn std::nullopt;\n+\n+\tchar *end;\n+\n+\terrno = 0;\n+\tdouble value = utils::strtod(obj.value_.c_str(), &end);\n+\n+\tif ('\\0' != *end || errno == ERANGE)\n+\t\treturn std::nullopt;\n+\n+\treturn value;\n+}\n+\n+template<>\n+void YamlObject::Accessor<double>::set(YamlObject &obj, double value)\n+{\n+\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n+\t\treturn;\n+\n+\tobj.type_ = Type::Value;\n+\tobj.value_ = std::to_string(value);\n+}\n+\n+template<>\n+std::optional<std::string>\n+YamlObject::Accessor<std::string>::get(const YamlObject &obj) const\n+{\n+\tif (obj.type_ != Type::Value)\n+\t\treturn std::nullopt;\n+\n+\treturn obj.value_;\n+}\n+\n+template<>\n+void YamlObject::Accessor<std::string>::set(YamlObject &obj, std::string value)\n+{\n+\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n+\t\treturn;\n+\n+\tobj.type_ = Type::Value;\n+\tobj.value_ = std::move(value);\n+}\n+\n+template<typename T>\n+struct YamlObject::Accessor<std::vector<T>, std::enable_if_t<\n+\tstd::is_same_v<bool, T> ||\n+\tstd::is_same_v<float, T> ||\n+\tstd::is_same_v<double, T> ||\n+\tstd::is_same_v<int8_t, T> ||\n+\tstd::is_same_v<uint8_t, T> ||\n+\tstd::is_same_v<int16_t, T> ||\n+\tstd::is_same_v<uint16_t, T> ||\n+\tstd::is_same_v<int32_t, T> ||\n+\tstd::is_same_v<uint32_t, T> ||\n+\tstd::is_same_v<std::string, T>>>\n+{\n+\tstd::optional<std::vector<T>> get(const YamlObject &obj) const\n+\t{\n+\t\tif (obj.type_ != Type::List)\n+\t\t\treturn std::nullopt;\n+\n+\t\tstd::vector<T> values;\n+\t\tvalues.reserve(obj.list_.size());\n+\n+\t\tfor (const YamlObject &entry : obj.asList()) {\n+\t\t\tconst auto value = entry.get<T>();\n+\t\t\tif (!value)\n+\t\t\t\treturn std::nullopt;\n+\t\t\tvalues.emplace_back(*value);\n+\t\t}\n+\n+\t\treturn values;\n+\t}\n+};\n+\n+template struct YamlObject::Accessor<std::vector<bool>>;\n+template struct YamlObject::Accessor<std::vector<float>>;\n+template struct YamlObject::Accessor<std::vector<double>>;\n+template struct YamlObject::Accessor<std::vector<int8_t>>;\n+template struct YamlObject::Accessor<std::vector<uint8_t>>;\n+template struct YamlObject::Accessor<std::vector<int16_t>>;\n+template struct YamlObject::Accessor<std::vector<uint16_t>>;\n+template struct YamlObject::Accessor<std::vector<int32_t>>;\n+template struct YamlObject::Accessor<std::vector<uint32_t>>;\n+template struct YamlObject::Accessor<std::vector<std::string>>;\n+#endif /* __DOXYGEN__ */\n+\n+/**\n+ * \\fn YamlObject::asDict() const\n+ * \\brief Wrap a dictionary YamlObject in an adapter that exposes iterators\n+ *\n+ * The YamlObject class doesn't directly implement iterators, as the iterator\n+ * type depends on whether the object is a Dictionary or List. This function\n+ * wraps a YamlObject of Dictionary type into an adapter that exposes\n+ * iterators, as well as begin() and end() functions, allowing usage of\n+ * range-based for loops with YamlObject. As YAML mappings are not ordered, the\n+ * iteration order is not specified.\n+ *\n+ * The iterator's value_type is a\n+ * <em>std::pair<const std::string &, const \\ref YamlObject &></em>.\n+ *\n+ * If the YamlObject is not of Dictionary type, the returned adapter operates\n+ * as an empty container.\n+ *\n+ * \\return An adapter of unspecified type compatible with range-based for loops\n+ */\n+\n+/**\n+ * \\fn YamlObject::asList() const\n+ * \\brief Wrap a list YamlObject in an adapter that exposes iterators\n+ *\n+ * The YamlObject class doesn't directly implement iterators, as the iterator\n+ * type depends on whether the object is a Dictionary or List. This function\n+ * wraps a YamlObject of List type into an adapter that exposes iterators, as\n+ * well as begin() and end() functions, allowing usage of range-based for loops\n+ * with YamlObject. As YAML lists are ordered, the iteration order is identical\n+ * to the list order in the YAML data.\n+ *\n+ * The iterator's value_type is a <em>const YamlObject &</em>.\n+ *\n+ * If the YamlObject is not of List type, the returned adapter operates as an\n+ * empty container.\n+ *\n+ * \\return An adapter of unspecified type compatible with range-based for loops\n+ */\n+\n+/**\n+ * \\fn YamlObject::operator[](std::size_t index) const\n+ * \\brief Retrieve the element from list YamlObject by index\n+ *\n+ * This function retrieves an element of the YamlObject. Only YamlObject\n+ * instances of List type associate elements with index, calling this function\n+ * on other types of instances or with an invalid index results in an empty\n+ * object.\n+ *\n+ * \\return The YamlObject as an element of the list\n+ */\n+const YamlObject &YamlObject::operator[](std::size_t index) const\n+{\n+\tif (type_ != Type::List || index >= size())\n+\t\treturn empty;\n+\n+\treturn *list_[index].value;\n+}\n+\n+/**\n+ * \\fn YamlObject::contains()\n+ * \\brief Check if an element of a dictionary exists\n+ *\n+ * This function check if the YamlObject contains an element. Only YamlObject\n+ * instances of Dictionary type associate elements with names, calling this\n+ * function on other types of instances is invalid and results in undefined\n+ * behaviour.\n+ *\n+ * \\return True if an element exists, false otherwise\n+ */\n+bool YamlObject::contains(std::string_view key) const\n+{\n+\treturn dictionary_.find(key) != dictionary_.end();\n+}\n+\n+/**\n+ * \\fn YamlObject::operator[](std::string_view key) const\n+ * \\brief Retrieve a member by name from the dictionary\n+ *\n+ * This function retrieve a member of a YamlObject by name. Only YamlObject\n+ * instances of Dictionary type associate elements with names, calling this\n+ * function on other types of instances or with a nonexistent key results in an\n+ * empty object.\n+ *\n+ * \\return The YamlObject corresponding to the \\a key member\n+ */\n+const YamlObject &YamlObject::operator[](std::string_view key) const\n+{\n+\tif (type_ != Type::Dictionary)\n+\t\treturn empty;\n+\n+\tauto iter = dictionary_.find(key);\n+\tif (iter == dictionary_.end())\n+\t\treturn empty;\n+\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 The child object if successfully added, nullptr 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+ * \\return The child object if successfully added, nullptr 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+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\nindex 0ef1f3ea303b..d83df0fb9597 100644\n--- a/src/libcamera/yaml_parser.cpp\n+++ b/src/libcamera/yaml_parser.cpp\n@@ -7,11 +7,10 @@\n \n #include \"libcamera/internal/yaml_parser.h\"\n \n-#include <charconv>\n #include <errno.h>\n #include <functional>\n-#include <limits>\n-#include <stdlib.h>\n+#include <memory>\n+#include <string>\n \n #include <libcamera/base/file.h>\n #include <libcamera/base/log.h>\n@@ -27,459 +26,6 @@ namespace libcamera {\n \n LOG_DEFINE_CATEGORY(YamlParser)\n \n-namespace {\n-\n-/* Empty static YamlObject as a safe result for invalid operations */\n-static const YamlObject empty;\n-\n-} /* namespace */\n-\n-/**\n- * \\class YamlObject\n- * \\brief A class representing the tree structure of the YAML content\n- *\n- * The YamlObject class represents the tree structure of YAML content. A\n- * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a\n- * tree leaf.\n- */\n-\n-YamlObject::YamlObject()\n-\t: type_(Type::Empty)\n-{\n-}\n-\n-YamlObject::~YamlObject() = default;\n-\n-/**\n- * \\fn YamlObject::isValue()\n- * \\brief Return whether the YamlObject is a value\n- *\n- * \\return True if the YamlObject is a value, false otherwise\n- */\n-\n-/**\n- * \\fn YamlObject::isList()\n- * \\brief Return whether the YamlObject is a list\n- *\n- * \\return True if the YamlObject is a list, false otherwise\n- */\n-\n-/**\n- * \\fn YamlObject::isDictionary()\n- * \\brief Return whether the YamlObject is a dictionary\n- *\n- * \\return True if the YamlObject is a dictionary, false otherwise\n- */\n-\n-/**\n- * \\fn YamlObject::isEmpty()\n- * \\brief Return whether the YamlObject is an empty\n- *\n- * \\return True if the YamlObject is empty, false otherwise\n- */\n-\n-/**\n- * \\fn YamlObject::operator bool()\n- * \\brief Return whether the YamlObject is a non-empty\n- *\n- * \\return False if the YamlObject is empty, true otherwise\n- */\n-\n-/**\n- * \\brief Retrieve the number of elements in a dictionary or list YamlObject\n- *\n- * This function retrieves the size of the YamlObject, defined as the number of\n- * child elements it contains. Only YamlObject instances of Dictionary or List\n- * types have a size, calling this function on other types of instances is\n- * invalid and results in undefined behaviour.\n- *\n- * \\return The size of the YamlObject\n- */\n-std::size_t YamlObject::size() const\n-{\n-\tswitch (type_) {\n-\tcase Type::Dictionary:\n-\tcase Type::List:\n-\t\treturn list_.size();\n-\tdefault:\n-\t\treturn 0;\n-\t}\n-}\n-\n-/**\n- * \\fn template<typename T> YamlObject::get<T>() const\n- * \\tparam T Type of the value\n- * \\brief Parse the YamlObject as a \\a T value\n- *\n- * This function parses the value of the YamlObject as a \\a T object, and\n- * returns the value. If parsing fails (usually because the YamlObject doesn't\n- * store a \\a T value), std::nullopt is returned.\n- *\n- * If the type \\a T is an std::vector, the YamlObject will be parsed as a list\n- * of values.\n- *\n- * \\return The YamlObject value, or std::nullopt if parsing failed\n- */\n-\n-/**\n- * \\fn template<typename T, typename U> YamlObject::get<T>(U &&defaultValue) const\n- * \\brief Parse the YamlObject as a \\a T value\n- * \\param[in] defaultValue The default value when failing to parse\n- *\n- * This function parses the value of the YamlObject as a \\a T object, and\n- * returns the value. If parsing fails (usually because the YamlObject doesn't\n- * store a \\a T value), the \\a defaultValue is returned.\n- *\n- * Unlike the get() function, this overload does not support std::vector for the\n- * type \\a T.\n- *\n- * \\return The YamlObject value, or \\a defaultValue if parsing failed\n- */\n-\n-/**\n- * \\fn template<typename T> YamlObject::set<T>(T &&value)\n- * \\brief Set the value of a YamlObject\n- * \\param[in] value The value\n- *\n- * This function sets the value stored in a YamlObject to \\a value. The value is\n- * converted to a string in an implementation-specific way that guarantees that\n- * subsequent calls to get<T>() will return the same value.\n- *\n- * Values can only be set on YamlObject of Type::Value type or empty YamlObject.\n- * Attempting to set a value on an object of type Type::Dict or Type::List does\n- * not modify the YamlObject.\n- */\n-\n-#ifndef __DOXYGEN__\n-\n-template<>\n-std::optional<bool>\n-YamlObject::Accessor<bool>::get(const YamlObject &obj) const\n-{\n-\tif (obj.type_ != Type::Value)\n-\t\treturn std::nullopt;\n-\n-\tif (obj.value_ == \"true\")\n-\t\treturn true;\n-\telse if (obj.value_ == \"false\")\n-\t\treturn false;\n-\n-\treturn std::nullopt;\n-}\n-\n-template<>\n-void YamlObject::Accessor<bool>::set(YamlObject &obj, bool value)\n-{\n-\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n-\t\treturn;\n-\n-\tobj.type_ = Type::Value;\n-\tobj.value_ = value ? \"true\" : \"false\";\n-}\n-\n-template<typename T>\n-struct YamlObject::Accessor<T, std::enable_if_t<\n-\tstd::is_same_v<int8_t, T> ||\n-\tstd::is_same_v<uint8_t, T> ||\n-\tstd::is_same_v<int16_t, T> ||\n-\tstd::is_same_v<uint16_t, T> ||\n-\tstd::is_same_v<int32_t, T> ||\n-\tstd::is_same_v<uint32_t, T>>>\n-{\n-\tstd::optional<T> get(const YamlObject &obj) const\n-\t{\n-\t\tif (obj.type_ != Type::Value)\n-\t\t\treturn std::nullopt;\n-\n-\t\tconst std::string &str = obj.value_;\n-\t\tT value;\n-\n-\t\tauto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(),\n-\t\t\t\t\t\t value);\n-\t\tif (ptr != str.data() + str.size() || ec != std::errc())\n-\t\t\treturn std::nullopt;\n-\n-\t\treturn value;\n-\t}\n-\n-\tvoid set(YamlObject &obj, T value)\n-\t{\n-\t\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n-\t\t\treturn;\n-\n-\t\tobj.type_ = Type::Value;\n-\t\tobj.value_ = std::to_string(value);\n-\t}\n-};\n-\n-template struct YamlObject::Accessor<int8_t>;\n-template struct YamlObject::Accessor<uint8_t>;\n-template struct YamlObject::Accessor<int16_t>;\n-template struct YamlObject::Accessor<uint16_t>;\n-template struct YamlObject::Accessor<int32_t>;\n-template struct YamlObject::Accessor<uint32_t>;\n-\n-template<>\n-std::optional<float>\n-YamlObject::Accessor<float>::get(const YamlObject &obj) const\n-{\n-\treturn obj.get<double>();\n-}\n-\n-template<>\n-void YamlObject::Accessor<float>::set(YamlObject &obj, float value)\n-{\n-\tobj.set<double>(std::forward<float>(value));\n-}\n-\n-template<>\n-std::optional<double>\n-YamlObject::Accessor<double>::get(const YamlObject &obj) const\n-{\n-\tif (obj.type_ != Type::Value)\n-\t\treturn std::nullopt;\n-\n-\tif (obj.value_.empty())\n-\t\treturn std::nullopt;\n-\n-\tchar *end;\n-\n-\terrno = 0;\n-\tdouble value = utils::strtod(obj.value_.c_str(), &end);\n-\n-\tif ('\\0' != *end || errno == ERANGE)\n-\t\treturn std::nullopt;\n-\n-\treturn value;\n-}\n-\n-template<>\n-void YamlObject::Accessor<double>::set(YamlObject &obj, double value)\n-{\n-\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n-\t\treturn;\n-\n-\tobj.type_ = Type::Value;\n-\tobj.value_ = std::to_string(value);\n-}\n-\n-template<>\n-std::optional<std::string>\n-YamlObject::Accessor<std::string>::get(const YamlObject &obj) const\n-{\n-\tif (obj.type_ != Type::Value)\n-\t\treturn std::nullopt;\n-\n-\treturn obj.value_;\n-}\n-\n-template<>\n-void YamlObject::Accessor<std::string>::set(YamlObject &obj, std::string value)\n-{\n-\tif (obj.type_ != Type::Empty && obj.type_ != Type::Value)\n-\t\treturn;\n-\n-\tobj.type_ = Type::Value;\n-\tobj.value_ = std::move(value);\n-}\n-\n-template<typename T>\n-struct YamlObject::Accessor<std::vector<T>, std::enable_if_t<\n-\tstd::is_same_v<bool, T> ||\n-\tstd::is_same_v<float, T> ||\n-\tstd::is_same_v<double, T> ||\n-\tstd::is_same_v<int8_t, T> ||\n-\tstd::is_same_v<uint8_t, T> ||\n-\tstd::is_same_v<int16_t, T> ||\n-\tstd::is_same_v<uint16_t, T> ||\n-\tstd::is_same_v<int32_t, T> ||\n-\tstd::is_same_v<uint32_t, T> ||\n-\tstd::is_same_v<std::string, T>>>\n-{\n-\tstd::optional<std::vector<T>> get(const YamlObject &obj) const\n-\t{\n-\t\tif (obj.type_ != Type::List)\n-\t\t\treturn std::nullopt;\n-\n-\t\tstd::vector<T> values;\n-\t\tvalues.reserve(obj.list_.size());\n-\n-\t\tfor (const YamlObject &entry : obj.asList()) {\n-\t\t\tconst auto value = entry.get<T>();\n-\t\t\tif (!value)\n-\t\t\t\treturn std::nullopt;\n-\t\t\tvalues.emplace_back(*value);\n-\t\t}\n-\n-\t\treturn values;\n-\t}\n-};\n-\n-template struct YamlObject::Accessor<std::vector<bool>>;\n-template struct YamlObject::Accessor<std::vector<float>>;\n-template struct YamlObject::Accessor<std::vector<double>>;\n-template struct YamlObject::Accessor<std::vector<int8_t>>;\n-template struct YamlObject::Accessor<std::vector<uint8_t>>;\n-template struct YamlObject::Accessor<std::vector<int16_t>>;\n-template struct YamlObject::Accessor<std::vector<uint16_t>>;\n-template struct YamlObject::Accessor<std::vector<int32_t>>;\n-template struct YamlObject::Accessor<std::vector<uint32_t>>;\n-template struct YamlObject::Accessor<std::vector<std::string>>;\n-#endif /* __DOXYGEN__ */\n-\n-/**\n- * \\fn YamlObject::asDict() const\n- * \\brief Wrap a dictionary YamlObject in an adapter that exposes iterators\n- *\n- * The YamlObject class doesn't directly implement iterators, as the iterator\n- * type depends on whether the object is a Dictionary or List. This function\n- * wraps a YamlObject of Dictionary type into an adapter that exposes\n- * iterators, as well as begin() and end() functions, allowing usage of\n- * range-based for loops with YamlObject. As YAML mappings are not ordered, the\n- * iteration order is not specified.\n- *\n- * The iterator's value_type is a\n- * <em>std::pair<const std::string &, const \\ref YamlObject &></em>.\n- *\n- * If the YamlObject is not of Dictionary type, the returned adapter operates\n- * as an empty container.\n- *\n- * \\return An adapter of unspecified type compatible with range-based for loops\n- */\n-\n-/**\n- * \\fn YamlObject::asList() const\n- * \\brief Wrap a list YamlObject in an adapter that exposes iterators\n- *\n- * The YamlObject class doesn't directly implement iterators, as the iterator\n- * type depends on whether the object is a Dictionary or List. This function\n- * wraps a YamlObject of List type into an adapter that exposes iterators, as\n- * well as begin() and end() functions, allowing usage of range-based for loops\n- * with YamlObject. As YAML lists are ordered, the iteration order is identical\n- * to the list order in the YAML data.\n- *\n- * The iterator's value_type is a <em>const YamlObject &</em>.\n- *\n- * If the YamlObject is not of List type, the returned adapter operates as an\n- * empty container.\n- *\n- * \\return An adapter of unspecified type compatible with range-based for loops\n- */\n-\n-/**\n- * \\fn YamlObject::operator[](std::size_t index) const\n- * \\brief Retrieve the element from list YamlObject by index\n- *\n- * This function retrieves an element of the YamlObject. Only YamlObject\n- * instances of List type associate elements with index, calling this function\n- * on other types of instances or with an invalid index results in an empty\n- * object.\n- *\n- * \\return The YamlObject as an element of the list\n- */\n-const YamlObject &YamlObject::operator[](std::size_t index) const\n-{\n-\tif (type_ != Type::List || index >= size())\n-\t\treturn empty;\n-\n-\treturn *list_[index].value;\n-}\n-\n-/**\n- * \\fn YamlObject::contains()\n- * \\brief Check if an element of a dictionary exists\n- *\n- * This function check if the YamlObject contains an element. Only YamlObject\n- * instances of Dictionary type associate elements with names, calling this\n- * function on other types of instances is invalid and results in undefined\n- * behaviour.\n- *\n- * \\return True if an element exists, false otherwise\n- */\n-bool YamlObject::contains(std::string_view key) const\n-{\n-\treturn dictionary_.find(key) != dictionary_.end();\n-}\n-\n-/**\n- * \\fn YamlObject::operator[](std::string_view key) const\n- * \\brief Retrieve a member by name from the dictionary\n- *\n- * This function retrieve a member of a YamlObject by name. Only YamlObject\n- * instances of Dictionary type associate elements with names, calling this\n- * function on other types of instances or with a nonexistent key results in an\n- * empty object.\n- *\n- * \\return The YamlObject corresponding to the \\a key member\n- */\n-const YamlObject &YamlObject::operator[](std::string_view key) const\n-{\n-\tif (type_ != Type::Dictionary)\n-\t\treturn empty;\n-\n-\tauto iter = dictionary_.find(key);\n-\tif (iter == dictionary_.end())\n-\t\treturn empty;\n-\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- * \\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-\n #ifndef __DOXYGEN__\n \n class YamlParserContext\n",
    "prefixes": [
        "17/36"
    ]
}