[17/36] libcamera: yaml_parser: Split YamlObject from YamlParser
diff mbox series

Message ID 20260113000808.15395-18-laurent.pinchart@ideasonboard.com
State New
Headers show
Series
  • libcamera: Global configuration file improvements
Related show

Commit Message

Laurent Pinchart Jan. 13, 2026, 12:07 a.m. UTC
The YamlObject class was designed to represent data parsed from YAML
files. It is outgrowing the initial needs. Move it to a separate file to
prepare for a rename.

Most files that include yaml_parser.h only need access to a YamlObject.
Switch them to yaml_object.h. pipeline_base.cpp was including
yaml_parser.h indirectly through pipeline_base.h, include it directly
now.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 .../libcamera/internal/global_configuration.h |   2 +-
 include/libcamera/internal/matrix.h           |   2 +-
 include/libcamera/internal/meson.build        |   1 +
 include/libcamera/internal/vector.h           |   2 +-
 include/libcamera/internal/yaml_object.h      | 226 +++++++++
 include/libcamera/internal/yaml_parser.h      | 212 +-------
 src/ipa/libipa/agc_mean_luminance.h           |   2 +-
 src/ipa/libipa/awb.h                          |   2 +-
 src/ipa/libipa/awb_bayes.h                    |   2 +-
 src/ipa/libipa/interpolator.cpp               |   2 -
 src/ipa/libipa/interpolator.h                 |   2 +-
 src/ipa/libipa/lsc_polynomial.h               |   2 +-
 src/ipa/libipa/lux.cpp                        |   2 +-
 src/ipa/libipa/module.h                       |   2 +-
 src/ipa/mali-c55/algorithms/blc.cpp           |   2 +-
 src/ipa/mali-c55/algorithms/lsc.cpp           |   2 +-
 src/ipa/rkisp1/algorithms/agc.cpp             |   2 +-
 src/ipa/rkisp1/algorithms/blc.cpp             |   2 +-
 src/ipa/rkisp1/algorithms/ccm.cpp             |   2 +-
 src/ipa/rkisp1/algorithms/dpcc.cpp            |   2 +-
 src/ipa/rkisp1/algorithms/goc.cpp             |   2 +-
 src/ipa/rkisp1/algorithms/gsl.cpp             |   2 +-
 src/ipa/rkisp1/algorithms/lsc.cpp             |   2 +-
 src/ipa/rkisp1/algorithms/wdr.cpp             |   2 +-
 src/ipa/rpi/controller/algorithm.h            |   2 +-
 src/ipa/rpi/controller/controller.h           |   2 +-
 src/libcamera/geometry.cpp                    |   2 +-
 src/libcamera/meson.build                     |   1 +
 .../pipeline/rpi/common/pipeline_base.cpp     |   1 +
 .../pipeline/rpi/common/pipeline_base.h       |   2 +-
 .../pipeline/virtual/config_parser.h          |   2 +-
 src/libcamera/pipeline/virtual/virtual.cpp    |   2 +-
 src/libcamera/yaml_object.cpp                 | 477 ++++++++++++++++++
 src/libcamera/yaml_parser.cpp                 | 458 +----------------
 34 files changed, 736 insertions(+), 694 deletions(-)
 create mode 100644 include/libcamera/internal/yaml_object.h
 create mode 100644 src/libcamera/yaml_object.cpp

Patch
diff mbox series

diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h
index 8d09517edca1..16c6a21f2a8a 100644
--- a/include/libcamera/internal/global_configuration.h
+++ b/include/libcamera/internal/global_configuration.h
@@ -14,7 +14,7 @@ 
 
 #include <libcamera/base/utils.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 namespace libcamera {
 
diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h
index 67761cb6037d..11fccd27547e 100644
--- a/include/libcamera/internal/matrix.h
+++ b/include/libcamera/internal/matrix.h
@@ -14,7 +14,7 @@ 
 #include <libcamera/base/log.h>
 #include <libcamera/base/span.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 namespace libcamera {
 
diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
index dc48619e5ac8..c24c9f062f29 100644
--- a/include/libcamera/internal/meson.build
+++ b/include/libcamera/internal/meson.build
@@ -50,6 +50,7 @@  libcamera_internal_headers = files([
     'v4l2_subdevice.h',
     'v4l2_videodevice.h',
     'vector.h',
+    'yaml_object.h',
     'yaml_parser.h',
 ])
 
diff --git a/include/libcamera/internal/vector.h b/include/libcamera/internal/vector.h
index cfd8882ce0e6..af24485f3bb1 100644
--- a/include/libcamera/internal/vector.h
+++ b/include/libcamera/internal/vector.h
@@ -19,7 +19,7 @@ 
 #include <libcamera/base/span.h>
 
 #include "libcamera/internal/matrix.h"
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 namespace libcamera {
 
diff --git a/include/libcamera/internal/yaml_object.h b/include/libcamera/internal/yaml_object.h
new file mode 100644
index 000000000000..1771f8821e2e
--- /dev/null
+++ b/include/libcamera/internal/yaml_object.h
@@ -0,0 +1,226 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022, Google Inc.
+ * Copyright (C) 2025, Ideas on Board
+ *
+ * libcamera YAML object
+ */
+
+#pragma once
+
+#include <iterator>
+#include <map>
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <libcamera/base/class.h>
+
+namespace libcamera {
+
+class YamlObject
+{
+private:
+	struct Value {
+		Value(std::string &&k, std::unique_ptr<YamlObject> &&v)
+			: key(std::move(k)), value(std::move(v))
+		{
+		}
+		std::string key;
+		std::unique_ptr<YamlObject> value;
+	};
+
+	using ValueContainer = std::vector<Value>;
+
+public:
+#ifndef __DOXYGEN__
+	template<typename Derived>
+	class Iterator
+	{
+	public:
+		using difference_type = std::ptrdiff_t;
+		using iterator_category = std::forward_iterator_tag;
+
+		Iterator(typename ValueContainer::const_iterator it)
+			: it_(it)
+		{
+		}
+
+		Derived &operator++()
+		{
+			++it_;
+			return *static_cast<Derived *>(this);
+		}
+
+		Derived operator++(int)
+		{
+			Derived it = *static_cast<Derived *>(this);
+			it_++;
+			return it;
+		}
+
+		friend bool operator==(const Iterator &a, const Iterator &b)
+		{
+			return a.it_ == b.it_;
+		}
+
+		friend bool operator!=(const Iterator &a, const Iterator &b)
+		{
+			return a.it_ != b.it_;
+		}
+
+	protected:
+		ValueContainer::const_iterator it_;
+	};
+
+	template<typename Iterator>
+	class Adapter
+	{
+	public:
+		Adapter(const ValueContainer &container)
+			: container_(container)
+		{
+		}
+
+		Iterator begin() const
+		{
+			return Iterator{ container_.begin() };
+		}
+
+		Iterator end() const
+		{
+			return Iterator{ container_.end() };
+		}
+
+	protected:
+		const ValueContainer &container_;
+	};
+
+	class ListIterator : public Iterator<ListIterator>
+	{
+	public:
+		using value_type = const YamlObject &;
+		using pointer = const YamlObject *;
+		using reference = value_type;
+
+		value_type operator*() const
+		{
+			return *it_->value.get();
+		}
+
+		pointer operator->() const
+		{
+			return it_->value.get();
+		}
+	};
+
+	class DictIterator : public Iterator<DictIterator>
+	{
+	public:
+		using value_type = std::pair<const std::string &, const YamlObject &>;
+		using pointer = value_type *;
+		using reference = value_type &;
+
+		value_type operator*() const
+		{
+			return { it_->key, *it_->value.get() };
+		}
+	};
+
+	class DictAdapter : public Adapter<DictIterator>
+	{
+	public:
+		using key_type = std::string;
+	};
+
+	class ListAdapter : public Adapter<ListIterator>
+	{
+	};
+#endif /* __DOXYGEN__ */
+
+	YamlObject();
+	~YamlObject();
+
+	bool isValue() const
+	{
+		return type_ == Type::Value;
+	}
+	bool isList() const
+	{
+		return type_ == Type::List;
+	}
+	bool isDictionary() const
+	{
+		return type_ == Type::Dictionary;
+	}
+	bool isEmpty() const
+	{
+		return type_ == Type::Empty;
+	}
+	explicit operator bool() const
+	{
+		return type_ != Type::Empty;
+	}
+
+	std::size_t size() const;
+
+	template<typename T>
+	std::optional<T> get() const
+	{
+		return Accessor<T>{}.get(*this);
+	}
+
+	template<typename T, typename U>
+	T get(U &&defaultValue) const
+	{
+		return get<T>().value_or(std::forward<U>(defaultValue));
+	}
+
+	template<typename T>
+	void set(T &&value)
+	{
+		return Accessor<std::remove_reference_t<T>>{}.set(*this, std::forward<T>(value));
+	}
+
+	DictAdapter asDict() const { return DictAdapter{ list_ }; }
+	ListAdapter asList() const { return ListAdapter{ list_ }; }
+
+	const YamlObject &operator[](std::size_t index) const;
+
+	bool contains(std::string_view key) const;
+	const YamlObject &operator[](std::string_view key) const;
+
+	YamlObject *add(std::unique_ptr<YamlObject> child);
+	YamlObject *add(std::string key, std::unique_ptr<YamlObject> child);
+
+private:
+	LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
+
+	template<typename T>
+	friend struct Accessor;
+
+	enum class Type {
+		Dictionary,
+		List,
+		Value,
+		Empty,
+	};
+
+	template<typename T, typename Enable = void>
+	struct Accessor {
+		std::optional<T> get(const YamlObject &obj) const;
+		void set(YamlObject &obj, T value);
+	};
+
+	Type type_;
+
+	std::string value_;
+	ValueContainer list_;
+	std::map<std::string, YamlObject *, std::less<>> dictionary_;
+};
+
+} /* namespace libcamera */
diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
index 7b6d16de1a04..e503e83a80da 100644
--- a/include/libcamera/internal/yaml_parser.h
+++ b/include/libcamera/internal/yaml_parser.h
@@ -7,221 +7,13 @@ 
 
 #pragma once
 
-#include <iterator>
-#include <map>
-#include <optional>
-#include <stdint.h>
-#include <string>
-#include <string_view>
-#include <vector>
+#include <memory>
 
-#include <libcamera/base/class.h>
+#include "libcamera/internal/yaml_object.h"
 
 namespace libcamera {
 
 class File;
-class YamlParserContext;
-
-class YamlObject
-{
-private:
-	struct Value {
-		Value(std::string &&k, std::unique_ptr<YamlObject> &&v)
-			: key(std::move(k)), value(std::move(v))
-		{
-		}
-		std::string key;
-		std::unique_ptr<YamlObject> value;
-	};
-
-	using ValueContainer = std::vector<Value>;
-
-public:
-#ifndef __DOXYGEN__
-	template<typename Derived>
-	class Iterator
-	{
-	public:
-		using difference_type = std::ptrdiff_t;
-		using iterator_category = std::forward_iterator_tag;
-
-		Iterator(typename ValueContainer::const_iterator it)
-			: it_(it)
-		{
-		}
-
-		Derived &operator++()
-		{
-			++it_;
-			return *static_cast<Derived *>(this);
-		}
-
-		Derived operator++(int)
-		{
-			Derived it = *static_cast<Derived *>(this);
-			it_++;
-			return it;
-		}
-
-		friend bool operator==(const Iterator &a, const Iterator &b)
-		{
-			return a.it_ == b.it_;
-		}
-
-		friend bool operator!=(const Iterator &a, const Iterator &b)
-		{
-			return a.it_ != b.it_;
-		}
-
-	protected:
-		ValueContainer::const_iterator it_;
-	};
-
-	template<typename Iterator>
-	class Adapter
-	{
-	public:
-		Adapter(const ValueContainer &container)
-			: container_(container)
-		{
-		}
-
-		Iterator begin() const
-		{
-			return Iterator{ container_.begin() };
-		}
-
-		Iterator end() const
-		{
-			return Iterator{ container_.end() };
-		}
-
-	protected:
-		const ValueContainer &container_;
-	};
-
-	class ListIterator : public Iterator<ListIterator>
-	{
-	public:
-		using value_type = const YamlObject &;
-		using pointer = const YamlObject *;
-		using reference = value_type;
-
-		value_type operator*() const
-		{
-			return *it_->value.get();
-		}
-
-		pointer operator->() const
-		{
-			return it_->value.get();
-		}
-	};
-
-	class DictIterator : public Iterator<DictIterator>
-	{
-	public:
-		using value_type = std::pair<const std::string &, const YamlObject &>;
-		using pointer = value_type *;
-		using reference = value_type &;
-
-		value_type operator*() const
-		{
-			return { it_->key, *it_->value.get() };
-		}
-	};
-
-	class DictAdapter : public Adapter<DictIterator>
-	{
-	public:
-		using key_type = std::string;
-	};
-
-	class ListAdapter : public Adapter<ListIterator>
-	{
-	};
-#endif /* __DOXYGEN__ */
-
-	YamlObject();
-	~YamlObject();
-
-	bool isValue() const
-	{
-		return type_ == Type::Value;
-	}
-	bool isList() const
-	{
-		return type_ == Type::List;
-	}
-	bool isDictionary() const
-	{
-		return type_ == Type::Dictionary;
-	}
-	bool isEmpty() const
-	{
-		return type_ == Type::Empty;
-	}
-	explicit operator bool() const
-	{
-		return type_ != Type::Empty;
-	}
-
-	std::size_t size() const;
-
-	template<typename T>
-	std::optional<T> get() const
-	{
-		return Accessor<T>{}.get(*this);
-	}
-
-	template<typename T, typename U>
-	T get(U &&defaultValue) const
-	{
-		return get<T>().value_or(std::forward<U>(defaultValue));
-	}
-
-	template<typename T>
-	void set(T &&value)
-	{
-		return Accessor<std::remove_reference_t<T>>{}.set(*this, std::forward<T>(value));
-	}
-
-	DictAdapter asDict() const { return DictAdapter{ list_ }; }
-	ListAdapter asList() const { return ListAdapter{ list_ }; }
-
-	const YamlObject &operator[](std::size_t index) const;
-
-	bool contains(std::string_view key) const;
-	const YamlObject &operator[](std::string_view key) const;
-
-	YamlObject *add(std::unique_ptr<YamlObject> child);
-	YamlObject *add(std::string key, std::unique_ptr<YamlObject> child);
-
-private:
-	LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
-
-	template<typename T>
-	friend struct Accessor;
-
-	enum class Type {
-		Dictionary,
-		List,
-		Value,
-		Empty,
-	};
-
-	template<typename T, typename Enable = void>
-	struct Accessor {
-		std::optional<T> get(const YamlObject &obj) const;
-		void set(YamlObject &obj, T value);
-	};
-
-	Type type_;
-
-	std::string value_;
-	ValueContainer list_;
-	std::map<std::string, YamlObject *, std::less<>> dictionary_;
-};
 
 class YamlParser final
 {
diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h
index 0df97b27332d..dfe1ccbe948b 100644
--- a/src/ipa/libipa/agc_mean_luminance.h
+++ b/src/ipa/libipa/agc_mean_luminance.h
@@ -16,7 +16,7 @@ 
 
 #include <libcamera/controls.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "exposure_mode_helper.h"
 #include "histogram.h"
diff --git a/src/ipa/libipa/awb.h b/src/ipa/libipa/awb.h
index f4a86038635f..3f25d13feaa8 100644
--- a/src/ipa/libipa/awb.h
+++ b/src/ipa/libipa/awb.h
@@ -14,7 +14,7 @@ 
 #include <libcamera/controls.h>
 
 #include "libcamera/internal/vector.h"
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 namespace libcamera {
 
diff --git a/src/ipa/libipa/awb_bayes.h b/src/ipa/libipa/awb_bayes.h
index 47ef3cce4d58..79334ad3e7a3 100644
--- a/src/ipa/libipa/awb_bayes.h
+++ b/src/ipa/libipa/awb_bayes.h
@@ -10,7 +10,7 @@ 
 #include <libcamera/controls.h>
 
 #include "libcamera/internal/vector.h"
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "awb.h"
 #include "interpolator.h"
diff --git a/src/ipa/libipa/interpolator.cpp b/src/ipa/libipa/interpolator.cpp
index f901a86e4c74..9355802f796a 100644
--- a/src/ipa/libipa/interpolator.cpp
+++ b/src/ipa/libipa/interpolator.cpp
@@ -11,8 +11,6 @@ 
 
 #include <libcamera/base/log.h>
 
-#include "libcamera/internal/yaml_parser.h"
-
 #include "interpolator.h"
 
 /**
diff --git a/src/ipa/libipa/interpolator.h b/src/ipa/libipa/interpolator.h
index 7880db6976d1..08003a06dbba 100644
--- a/src/ipa/libipa/interpolator.h
+++ b/src/ipa/libipa/interpolator.h
@@ -15,7 +15,7 @@ 
 
 #include <libcamera/base/log.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 namespace libcamera {
 
diff --git a/src/ipa/libipa/lsc_polynomial.h b/src/ipa/libipa/lsc_polynomial.h
index c71f215de3fc..19da46860849 100644
--- a/src/ipa/libipa/lsc_polynomial.h
+++ b/src/ipa/libipa/lsc_polynomial.h
@@ -16,7 +16,7 @@ 
 
 #include <libcamera/geometry.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 namespace libcamera {
 
diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp
index 899e88248f04..d79b116a3339 100644
--- a/src/ipa/libipa/lux.cpp
+++ b/src/ipa/libipa/lux.cpp
@@ -12,7 +12,7 @@ 
 
 #include <libcamera/base/log.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "histogram.h"
 
diff --git a/src/ipa/libipa/module.h b/src/ipa/libipa/module.h
index c27af7718feb..8e6c658a6b63 100644
--- a/src/ipa/libipa/module.h
+++ b/src/ipa/libipa/module.h
@@ -15,7 +15,7 @@ 
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "algorithm.h"
 
diff --git a/src/ipa/mali-c55/algorithms/blc.cpp b/src/ipa/mali-c55/algorithms/blc.cpp
index d099219c3e43..3bdf19141e1d 100644
--- a/src/ipa/mali-c55/algorithms/blc.cpp
+++ b/src/ipa/mali-c55/algorithms/blc.cpp
@@ -10,7 +10,7 @@ 
 #include <libcamera/base/log.h>
 #include <libcamera/control_ids.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 /**
  * \file blc.h
diff --git a/src/ipa/mali-c55/algorithms/lsc.cpp b/src/ipa/mali-c55/algorithms/lsc.cpp
index f75b9cd7b7b8..e32f5a83485e 100644
--- a/src/ipa/mali-c55/algorithms/lsc.cpp
+++ b/src/ipa/mali-c55/algorithms/lsc.cpp
@@ -7,7 +7,7 @@ 
 
 #include "lsc.h"
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 namespace libcamera {
 
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index 7cc06f91ac2b..ef16a3775056 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -19,7 +19,7 @@ 
 #include <libcamera/control_ids.h>
 #include <libcamera/ipa/core_ipa_interface.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "libipa/histogram.h"
 
diff --git a/src/ipa/rkisp1/algorithms/blc.cpp b/src/ipa/rkisp1/algorithms/blc.cpp
index 32fc44ffff92..19c262fffa73 100644
--- a/src/ipa/rkisp1/algorithms/blc.cpp
+++ b/src/ipa/rkisp1/algorithms/blc.cpp
@@ -13,7 +13,7 @@ 
 
 #include <libcamera/control_ids.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 /**
  * \file blc.h
diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp
index de2b6fe775aa..3ed307280677 100644
--- a/src/ipa/rkisp1/algorithms/ccm.cpp
+++ b/src/ipa/rkisp1/algorithms/ccm.cpp
@@ -16,7 +16,7 @@ 
 
 #include <libcamera/ipa/core_ipa_interface.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "libipa/fixedpoint.h"
 #include "libipa/interpolator.h"
diff --git a/src/ipa/rkisp1/algorithms/dpcc.cpp b/src/ipa/rkisp1/algorithms/dpcc.cpp
index 7894628144f3..c195334750e1 100644
--- a/src/ipa/rkisp1/algorithms/dpcc.cpp
+++ b/src/ipa/rkisp1/algorithms/dpcc.cpp
@@ -9,7 +9,7 @@ 
 
 #include <libcamera/base/log.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "linux/rkisp1-config.h"
 
diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp
index a0e7030fe5db..6c07bd8c71e5 100644
--- a/src/ipa/rkisp1/algorithms/goc.cpp
+++ b/src/ipa/rkisp1/algorithms/goc.cpp
@@ -13,7 +13,7 @@ 
 
 #include <libcamera/control_ids.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "linux/rkisp1-config.h"
 
diff --git a/src/ipa/rkisp1/algorithms/gsl.cpp b/src/ipa/rkisp1/algorithms/gsl.cpp
index 7ac5dc215850..292d0e80dc57 100644
--- a/src/ipa/rkisp1/algorithms/gsl.cpp
+++ b/src/ipa/rkisp1/algorithms/gsl.cpp
@@ -10,7 +10,7 @@ 
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "linux/rkisp1-config.h"
 
diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
index 429565a0b51f..9c0ed44b3943 100644
--- a/src/ipa/rkisp1/algorithms/lsc.cpp
+++ b/src/ipa/rkisp1/algorithms/lsc.cpp
@@ -14,7 +14,7 @@ 
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "libipa/lsc_polynomial.h"
 #include "linux/rkisp1-config.h"
diff --git a/src/ipa/rkisp1/algorithms/wdr.cpp b/src/ipa/rkisp1/algorithms/wdr.cpp
index ed81628c032c..9e2688a05179 100644
--- a/src/ipa/rkisp1/algorithms/wdr.cpp
+++ b/src/ipa/rkisp1/algorithms/wdr.cpp
@@ -10,7 +10,7 @@ 
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include <libipa/agc_mean_luminance.h>
 #include <libipa/histogram.h>
diff --git a/src/ipa/rpi/controller/algorithm.h b/src/ipa/rpi/controller/algorithm.h
index 1971bfdcc8ad..8839daa90916 100644
--- a/src/ipa/rpi/controller/algorithm.h
+++ b/src/ipa/rpi/controller/algorithm.h
@@ -15,7 +15,7 @@ 
 #include <memory>
 #include <map>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "controller.h"
 
diff --git a/src/ipa/rpi/controller/controller.h b/src/ipa/rpi/controller/controller.h
index fdb46557de9c..573942886bc0 100644
--- a/src/ipa/rpi/controller/controller.h
+++ b/src/ipa/rpi/controller/controller.h
@@ -16,7 +16,7 @@ 
 #include <string>
 
 #include <libcamera/base/utils.h>
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "camera_mode.h"
 #include "device_status.h"
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index 2e8e9e33ea78..621008844c74 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -12,7 +12,7 @@ 
 
 #include <libcamera/base/log.h>
 
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 /**
  * \file geometry.h
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index 6f952bd9832a..da89aa3714c3 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -58,6 +58,7 @@  libcamera_internal_sources = files([
     'v4l2_subdevice.cpp',
     'v4l2_videodevice.cpp',
     'vector.cpp',
+    'yaml_object.cpp',
     'yaml_parser.cpp',
 ])
 
diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
index 7eb197ce435c..684438bd5fb7 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
@@ -21,6 +21,7 @@ 
 
 #include "libcamera/internal/camera_lens.h"
 #include "libcamera/internal/v4l2_subdevice.h"
+#include "libcamera/internal/yaml_parser.h"
 
 using namespace std::chrono_literals;
 
diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h
index 6257a93467df..c69a690f580c 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.h
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h
@@ -26,7 +26,7 @@ 
 #include "libcamera/internal/pipeline_handler.h"
 #include "libcamera/internal/request.h"
 #include "libcamera/internal/v4l2_videodevice.h"
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include <libcamera/ipa/raspberrypi_ipa_interface.h>
 #include <libcamera/ipa/raspberrypi_ipa_proxy.h>
diff --git a/src/libcamera/pipeline/virtual/config_parser.h b/src/libcamera/pipeline/virtual/config_parser.h
index d2000de9c12f..f696d8862897 100644
--- a/src/libcamera/pipeline/virtual/config_parser.h
+++ b/src/libcamera/pipeline/virtual/config_parser.h
@@ -13,7 +13,7 @@ 
 #include <libcamera/base/file.h>
 
 #include "libcamera/internal/pipeline_handler.h"
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "virtual.h"
 
diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp
index 40c35264c568..b032db54ded4 100644
--- a/src/libcamera/pipeline/virtual/virtual.cpp
+++ b/src/libcamera/pipeline/virtual/virtual.cpp
@@ -36,7 +36,7 @@ 
 #include "libcamera/internal/framebuffer.h"
 #include "libcamera/internal/pipeline_handler.h"
 #include "libcamera/internal/request.h"
-#include "libcamera/internal/yaml_parser.h"
+#include "libcamera/internal/yaml_object.h"
 
 #include "pipeline/virtual/config_parser.h"
 
diff --git a/src/libcamera/yaml_object.cpp b/src/libcamera/yaml_object.cpp
new file mode 100644
index 000000000000..5e92189fdb8e
--- /dev/null
+++ b/src/libcamera/yaml_object.cpp
@@ -0,0 +1,477 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2022, Google Inc.
+ * Copyright (C) 2025, Ideas on Board.
+ *
+ * libcamera YAML object
+ */
+
+#include "libcamera/internal/yaml_object.h"
+
+#include <charconv>
+#include <errno.h>
+#include <string>
+#include <vector>
+
+#include <libcamera/base/utils.h>
+
+/**
+ * \file yaml_object.h
+ * \brief YAML objects
+ */
+
+namespace libcamera {
+
+namespace {
+
+/* Empty static YamlObject as a safe result for invalid operations */
+static const YamlObject empty;
+
+} /* namespace */
+
+/**
+ * \class YamlObject
+ * \brief A class representing the tree structure of the YAML content
+ *
+ * The YamlObject class represents the tree structure of YAML content. A
+ * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a
+ * tree leaf.
+ */
+
+YamlObject::YamlObject()
+	: type_(Type::Empty)
+{
+}
+
+YamlObject::~YamlObject() = default;
+
+/**
+ * \fn YamlObject::isValue()
+ * \brief Return whether the YamlObject is a value
+ *
+ * \return True if the YamlObject is a value, false otherwise
+ */
+
+/**
+ * \fn YamlObject::isList()
+ * \brief Return whether the YamlObject is a list
+ *
+ * \return True if the YamlObject is a list, false otherwise
+ */
+
+/**
+ * \fn YamlObject::isDictionary()
+ * \brief Return whether the YamlObject is a dictionary
+ *
+ * \return True if the YamlObject is a dictionary, false otherwise
+ */
+
+/**
+ * \fn YamlObject::isEmpty()
+ * \brief Return whether the YamlObject is an empty
+ *
+ * \return True if the YamlObject is empty, false otherwise
+ */
+
+/**
+ * \fn YamlObject::operator bool()
+ * \brief Return whether the YamlObject is a non-empty
+ *
+ * \return False if the YamlObject is empty, true otherwise
+ */
+
+/**
+ * \fn YamlObject::size()
+ * \brief Retrieve the number of elements in a dictionary or list YamlObject
+ *
+ * This function retrieves the size of the YamlObject, defined as the number of
+ * child elements it contains. Only YamlObject instances of Dictionary or List
+ * types have a size, calling this function on other types of instances is
+ * invalid and results in undefined behaviour.
+ *
+ * \return The size of the YamlObject
+ */
+std::size_t YamlObject::size() const
+{
+	switch (type_) {
+	case Type::Dictionary:
+	case Type::List:
+		return list_.size();
+	default:
+		return 0;
+	}
+}
+
+/**
+ * \fn template<typename T> YamlObject::get<T>() const
+ * \tparam T Type of the value
+ * \brief Parse the YamlObject as a \a T value
+ *
+ * This function parses the value of the YamlObject as a \a T object, and
+ * returns the value. If parsing fails (usually because the YamlObject doesn't
+ * store a \a T value), std::nullopt is returned.
+ *
+ * If the type \a T is an std::vector, the YamlObject will be parsed as a list
+ * of values.
+ *
+ * \return The YamlObject value, or std::nullopt if parsing failed
+ */
+
+/**
+ * \fn template<typename T, typename U> YamlObject::get<T>(U &&defaultValue) const
+ * \brief Parse the YamlObject as a \a T value
+ * \param[in] defaultValue The default value when failing to parse
+ *
+ * This function parses the value of the YamlObject as a \a T object, and
+ * returns the value. If parsing fails (usually because the YamlObject doesn't
+ * store a \a T value), the \a defaultValue is returned.
+ *
+ * Unlike the get() function, this overload does not support std::vector for the
+ * type \a T.
+ *
+ * \return The YamlObject value, or \a defaultValue if parsing failed
+ */
+
+/**
+ * \fn template<typename T> YamlObject::set<T>(T &&value)
+ * \brief Set the value of a YamlObject
+ * \param[in] value The value
+ *
+ * This function sets the value stored in a YamlObject to \a value. The value is
+ * converted to a string in an implementation-specific way that guarantees that
+ * subsequent calls to get<T>() will return the same value.
+ *
+ * Values can only be set on YamlObject of Type::Value type or empty YamlObject.
+ * Attempting to set a value on an object of type Type::Dict or Type::List does
+ * not modify the YamlObject.
+ */
+
+#ifndef __DOXYGEN__
+
+template<>
+std::optional<bool>
+YamlObject::Accessor<bool>::get(const YamlObject &obj) const
+{
+	if (obj.type_ != Type::Value)
+		return std::nullopt;
+
+	if (obj.value_ == "true")
+		return true;
+	else if (obj.value_ == "false")
+		return false;
+
+	return std::nullopt;
+}
+
+template<>
+void YamlObject::Accessor<bool>::set(YamlObject &obj, bool value)
+{
+	if (obj.type_ != Type::Empty && obj.type_ != Type::Value)
+		return;
+
+	obj.type_ = Type::Value;
+	obj.value_ = value ? "true" : "false";
+}
+
+template<typename T>
+struct YamlObject::Accessor<T, std::enable_if_t<
+	std::is_same_v<int8_t, T> ||
+	std::is_same_v<uint8_t, T> ||
+	std::is_same_v<int16_t, T> ||
+	std::is_same_v<uint16_t, T> ||
+	std::is_same_v<int32_t, T> ||
+	std::is_same_v<uint32_t, T>>>
+{
+	std::optional<T> get(const YamlObject &obj) const
+	{
+		if (obj.type_ != Type::Value)
+			return std::nullopt;
+
+		const std::string &str = obj.value_;
+		T value;
+
+		auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(),
+						 value);
+		if (ptr != str.data() + str.size() || ec != std::errc())
+			return std::nullopt;
+
+		return value;
+	}
+
+	void set(YamlObject &obj, T value)
+	{
+		if (obj.type_ != Type::Empty && obj.type_ != Type::Value)
+			return;
+
+		obj.type_ = Type::Value;
+		obj.value_ = std::to_string(value);
+	}
+};
+
+template struct YamlObject::Accessor<int8_t>;
+template struct YamlObject::Accessor<uint8_t>;
+template struct YamlObject::Accessor<int16_t>;
+template struct YamlObject::Accessor<uint16_t>;
+template struct YamlObject::Accessor<int32_t>;
+template struct YamlObject::Accessor<uint32_t>;
+
+template<>
+std::optional<float>
+YamlObject::Accessor<float>::get(const YamlObject &obj) const
+{
+	return obj.get<double>();
+}
+
+template<>
+void YamlObject::Accessor<float>::set(YamlObject &obj, float value)
+{
+	obj.set<double>(std::forward<float>(value));
+}
+
+template<>
+std::optional<double>
+YamlObject::Accessor<double>::get(const YamlObject &obj) const
+{
+	if (obj.type_ != Type::Value)
+		return std::nullopt;
+
+	if (obj.value_.empty())
+		return std::nullopt;
+
+	char *end;
+
+	errno = 0;
+	double value = utils::strtod(obj.value_.c_str(), &end);
+
+	if ('\0' != *end || errno == ERANGE)
+		return std::nullopt;
+
+	return value;
+}
+
+template<>
+void YamlObject::Accessor<double>::set(YamlObject &obj, double value)
+{
+	if (obj.type_ != Type::Empty && obj.type_ != Type::Value)
+		return;
+
+	obj.type_ = Type::Value;
+	obj.value_ = std::to_string(value);
+}
+
+template<>
+std::optional<std::string>
+YamlObject::Accessor<std::string>::get(const YamlObject &obj) const
+{
+	if (obj.type_ != Type::Value)
+		return std::nullopt;
+
+	return obj.value_;
+}
+
+template<>
+void YamlObject::Accessor<std::string>::set(YamlObject &obj, std::string value)
+{
+	if (obj.type_ != Type::Empty && obj.type_ != Type::Value)
+		return;
+
+	obj.type_ = Type::Value;
+	obj.value_ = std::move(value);
+}
+
+template<typename T>
+struct YamlObject::Accessor<std::vector<T>, std::enable_if_t<
+	std::is_same_v<bool, T> ||
+	std::is_same_v<float, T> ||
+	std::is_same_v<double, T> ||
+	std::is_same_v<int8_t, T> ||
+	std::is_same_v<uint8_t, T> ||
+	std::is_same_v<int16_t, T> ||
+	std::is_same_v<uint16_t, T> ||
+	std::is_same_v<int32_t, T> ||
+	std::is_same_v<uint32_t, T> ||
+	std::is_same_v<std::string, T>>>
+{
+	std::optional<std::vector<T>> get(const YamlObject &obj) const
+	{
+		if (obj.type_ != Type::List)
+			return std::nullopt;
+
+		std::vector<T> values;
+		values.reserve(obj.list_.size());
+
+		for (const YamlObject &entry : obj.asList()) {
+			const auto value = entry.get<T>();
+			if (!value)
+				return std::nullopt;
+			values.emplace_back(*value);
+		}
+
+		return values;
+	}
+};
+
+template struct YamlObject::Accessor<std::vector<bool>>;
+template struct YamlObject::Accessor<std::vector<float>>;
+template struct YamlObject::Accessor<std::vector<double>>;
+template struct YamlObject::Accessor<std::vector<int8_t>>;
+template struct YamlObject::Accessor<std::vector<uint8_t>>;
+template struct YamlObject::Accessor<std::vector<int16_t>>;
+template struct YamlObject::Accessor<std::vector<uint16_t>>;
+template struct YamlObject::Accessor<std::vector<int32_t>>;
+template struct YamlObject::Accessor<std::vector<uint32_t>>;
+template struct YamlObject::Accessor<std::vector<std::string>>;
+#endif /* __DOXYGEN__ */
+
+/**
+ * \fn YamlObject::asDict() const
+ * \brief Wrap a dictionary YamlObject in an adapter that exposes iterators
+ *
+ * The YamlObject class doesn't directly implement iterators, as the iterator
+ * type depends on whether the object is a Dictionary or List. This function
+ * wraps a YamlObject of Dictionary type into an adapter that exposes
+ * iterators, as well as begin() and end() functions, allowing usage of
+ * range-based for loops with YamlObject. As YAML mappings are not ordered, the
+ * iteration order is not specified.
+ *
+ * The iterator's value_type is a
+ * <em>std::pair<const std::string &, const \ref YamlObject &></em>.
+ *
+ * If the YamlObject is not of Dictionary type, the returned adapter operates
+ * as an empty container.
+ *
+ * \return An adapter of unspecified type compatible with range-based for loops
+ */
+
+/**
+ * \fn YamlObject::asList() const
+ * \brief Wrap a list YamlObject in an adapter that exposes iterators
+ *
+ * The YamlObject class doesn't directly implement iterators, as the iterator
+ * type depends on whether the object is a Dictionary or List. This function
+ * wraps a YamlObject of List type into an adapter that exposes iterators, as
+ * well as begin() and end() functions, allowing usage of range-based for loops
+ * with YamlObject. As YAML lists are ordered, the iteration order is identical
+ * to the list order in the YAML data.
+ *
+ * The iterator's value_type is a <em>const YamlObject &</em>.
+ *
+ * If the YamlObject is not of List type, the returned adapter operates as an
+ * empty container.
+ *
+ * \return An adapter of unspecified type compatible with range-based for loops
+ */
+
+/**
+ * \fn YamlObject::operator[](std::size_t index) const
+ * \brief Retrieve the element from list YamlObject by index
+ *
+ * This function retrieves an element of the YamlObject. Only YamlObject
+ * instances of List type associate elements with index, calling this function
+ * on other types of instances or with an invalid index results in an empty
+ * object.
+ *
+ * \return The YamlObject as an element of the list
+ */
+const YamlObject &YamlObject::operator[](std::size_t index) const
+{
+	if (type_ != Type::List || index >= size())
+		return empty;
+
+	return *list_[index].value;
+}
+
+/**
+ * \fn YamlObject::contains()
+ * \brief Check if an element of a dictionary exists
+ *
+ * This function check if the YamlObject contains an element. Only YamlObject
+ * instances of Dictionary type associate elements with names, calling this
+ * function on other types of instances is invalid and results in undefined
+ * behaviour.
+ *
+ * \return True if an element exists, false otherwise
+ */
+bool YamlObject::contains(std::string_view key) const
+{
+	return dictionary_.find(key) != dictionary_.end();
+}
+
+/**
+ * \fn YamlObject::operator[](std::string_view key) const
+ * \brief Retrieve a member by name from the dictionary
+ *
+ * This function retrieve a member of a YamlObject by name. Only YamlObject
+ * instances of Dictionary type associate elements with names, calling this
+ * function on other types of instances or with a nonexistent key results in an
+ * empty object.
+ *
+ * \return The YamlObject corresponding to the \a key member
+ */
+const YamlObject &YamlObject::operator[](std::string_view key) const
+{
+	if (type_ != Type::Dictionary)
+		return empty;
+
+	auto iter = dictionary_.find(key);
+	if (iter == dictionary_.end())
+		return empty;
+
+	return *iter->second;
+}
+
+/**
+ * \brief Add a child object to a list
+ * \param[in] child The child object
+ *
+ * Append the \a child node as the last element of this node's children list.
+ * This node must be empty, in which case it is converted to the Type::List
+ * type, or be a list. Otherwise, the \a child is discarded and the function
+ * returns a nullptr.
+ *
+ * \return The child object if successfully added, nullptr otherwise
+ */
+YamlObject *YamlObject::add(std::unique_ptr<YamlObject> child)
+{
+	if (type_ == Type::Empty)
+		type_ = Type::List;
+
+	if (type_ != Type::List)
+		return nullptr;
+
+	Value &elem = list_.emplace_back(std::string{}, std::move(child));
+	return elem.value.get();
+}
+
+/**
+ * \brief Add a child object to a dictionary
+ * \param[in] key The dictionary key
+ * \param[in] child The child object
+ *
+ * Add the \a child node with the given \a key to this node's children. This
+ * node must be empty, in which case it is converted to the Type::Dictionary
+ * type, or be a dictionary. Otherwise, the \a child is discarded and the
+ * function returns a nullptr.
+ *
+ * Keys are unique. If a child with the same \a key already exist, the \a child
+ * is discarded and the function returns a nullptr.
+ *
+ * \return The child object if successfully added, nullptr otherwise
+ */
+YamlObject *YamlObject::add(std::string key, std::unique_ptr<YamlObject> child)
+{
+	if (type_ == Type::Empty)
+		type_ = Type::Dictionary;
+
+	if (type_ != Type::Dictionary)
+		return nullptr;
+
+	if (dictionary_.find(key) != dictionary_.end())
+		return nullptr;
+
+	Value &elem = list_.emplace_back(std::move(key), std::move(child));
+	dictionary_.emplace(elem.key, elem.value.get());
+	return elem.value.get();
+}
+
+} /* namespace libcamera */
diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
index 0ef1f3ea303b..d83df0fb9597 100644
--- a/src/libcamera/yaml_parser.cpp
+++ b/src/libcamera/yaml_parser.cpp
@@ -7,11 +7,10 @@ 
 
 #include "libcamera/internal/yaml_parser.h"
 
-#include <charconv>
 #include <errno.h>
 #include <functional>
-#include <limits>
-#include <stdlib.h>
+#include <memory>
+#include <string>
 
 #include <libcamera/base/file.h>
 #include <libcamera/base/log.h>
@@ -27,459 +26,6 @@  namespace libcamera {
 
 LOG_DEFINE_CATEGORY(YamlParser)
 
-namespace {
-
-/* Empty static YamlObject as a safe result for invalid operations */
-static const YamlObject empty;
-
-} /* namespace */
-
-/**
- * \class YamlObject
- * \brief A class representing the tree structure of the YAML content
- *
- * The YamlObject class represents the tree structure of YAML content. A
- * YamlObject can be empty, a dictionary or list of YamlObjects, or a value if a
- * tree leaf.
- */
-
-YamlObject::YamlObject()
-	: type_(Type::Empty)
-{
-}
-
-YamlObject::~YamlObject() = default;
-
-/**
- * \fn YamlObject::isValue()
- * \brief Return whether the YamlObject is a value
- *
- * \return True if the YamlObject is a value, false otherwise
- */
-
-/**
- * \fn YamlObject::isList()
- * \brief Return whether the YamlObject is a list
- *
- * \return True if the YamlObject is a list, false otherwise
- */
-
-/**
- * \fn YamlObject::isDictionary()
- * \brief Return whether the YamlObject is a dictionary
- *
- * \return True if the YamlObject is a dictionary, false otherwise
- */
-
-/**
- * \fn YamlObject::isEmpty()
- * \brief Return whether the YamlObject is an empty
- *
- * \return True if the YamlObject is empty, false otherwise
- */
-
-/**
- * \fn YamlObject::operator bool()
- * \brief Return whether the YamlObject is a non-empty
- *
- * \return False if the YamlObject is empty, true otherwise
- */
-
-/**
- * \brief Retrieve the number of elements in a dictionary or list YamlObject
- *
- * This function retrieves the size of the YamlObject, defined as the number of
- * child elements it contains. Only YamlObject instances of Dictionary or List
- * types have a size, calling this function on other types of instances is
- * invalid and results in undefined behaviour.
- *
- * \return The size of the YamlObject
- */
-std::size_t YamlObject::size() const
-{
-	switch (type_) {
-	case Type::Dictionary:
-	case Type::List:
-		return list_.size();
-	default:
-		return 0;
-	}
-}
-
-/**
- * \fn template<typename T> YamlObject::get<T>() const
- * \tparam T Type of the value
- * \brief Parse the YamlObject as a \a T value
- *
- * This function parses the value of the YamlObject as a \a T object, and
- * returns the value. If parsing fails (usually because the YamlObject doesn't
- * store a \a T value), std::nullopt is returned.
- *
- * If the type \a T is an std::vector, the YamlObject will be parsed as a list
- * of values.
- *
- * \return The YamlObject value, or std::nullopt if parsing failed
- */
-
-/**
- * \fn template<typename T, typename U> YamlObject::get<T>(U &&defaultValue) const
- * \brief Parse the YamlObject as a \a T value
- * \param[in] defaultValue The default value when failing to parse
- *
- * This function parses the value of the YamlObject as a \a T object, and
- * returns the value. If parsing fails (usually because the YamlObject doesn't
- * store a \a T value), the \a defaultValue is returned.
- *
- * Unlike the get() function, this overload does not support std::vector for the
- * type \a T.
- *
- * \return The YamlObject value, or \a defaultValue if parsing failed
- */
-
-/**
- * \fn template<typename T> YamlObject::set<T>(T &&value)
- * \brief Set the value of a YamlObject
- * \param[in] value The value
- *
- * This function sets the value stored in a YamlObject to \a value. The value is
- * converted to a string in an implementation-specific way that guarantees that
- * subsequent calls to get<T>() will return the same value.
- *
- * Values can only be set on YamlObject of Type::Value type or empty YamlObject.
- * Attempting to set a value on an object of type Type::Dict or Type::List does
- * not modify the YamlObject.
- */
-
-#ifndef __DOXYGEN__
-
-template<>
-std::optional<bool>
-YamlObject::Accessor<bool>::get(const YamlObject &obj) const
-{
-	if (obj.type_ != Type::Value)
-		return std::nullopt;
-
-	if (obj.value_ == "true")
-		return true;
-	else if (obj.value_ == "false")
-		return false;
-
-	return std::nullopt;
-}
-
-template<>
-void YamlObject::Accessor<bool>::set(YamlObject &obj, bool value)
-{
-	if (obj.type_ != Type::Empty && obj.type_ != Type::Value)
-		return;
-
-	obj.type_ = Type::Value;
-	obj.value_ = value ? "true" : "false";
-}
-
-template<typename T>
-struct YamlObject::Accessor<T, std::enable_if_t<
-	std::is_same_v<int8_t, T> ||
-	std::is_same_v<uint8_t, T> ||
-	std::is_same_v<int16_t, T> ||
-	std::is_same_v<uint16_t, T> ||
-	std::is_same_v<int32_t, T> ||
-	std::is_same_v<uint32_t, T>>>
-{
-	std::optional<T> get(const YamlObject &obj) const
-	{
-		if (obj.type_ != Type::Value)
-			return std::nullopt;
-
-		const std::string &str = obj.value_;
-		T value;
-
-		auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(),
-						 value);
-		if (ptr != str.data() + str.size() || ec != std::errc())
-			return std::nullopt;
-
-		return value;
-	}
-
-	void set(YamlObject &obj, T value)
-	{
-		if (obj.type_ != Type::Empty && obj.type_ != Type::Value)
-			return;
-
-		obj.type_ = Type::Value;
-		obj.value_ = std::to_string(value);
-	}
-};
-
-template struct YamlObject::Accessor<int8_t>;
-template struct YamlObject::Accessor<uint8_t>;
-template struct YamlObject::Accessor<int16_t>;
-template struct YamlObject::Accessor<uint16_t>;
-template struct YamlObject::Accessor<int32_t>;
-template struct YamlObject::Accessor<uint32_t>;
-
-template<>
-std::optional<float>
-YamlObject::Accessor<float>::get(const YamlObject &obj) const
-{
-	return obj.get<double>();
-}
-
-template<>
-void YamlObject::Accessor<float>::set(YamlObject &obj, float value)
-{
-	obj.set<double>(std::forward<float>(value));
-}
-
-template<>
-std::optional<double>
-YamlObject::Accessor<double>::get(const YamlObject &obj) const
-{
-	if (obj.type_ != Type::Value)
-		return std::nullopt;
-
-	if (obj.value_.empty())
-		return std::nullopt;
-
-	char *end;
-
-	errno = 0;
-	double value = utils::strtod(obj.value_.c_str(), &end);
-
-	if ('\0' != *end || errno == ERANGE)
-		return std::nullopt;
-
-	return value;
-}
-
-template<>
-void YamlObject::Accessor<double>::set(YamlObject &obj, double value)
-{
-	if (obj.type_ != Type::Empty && obj.type_ != Type::Value)
-		return;
-
-	obj.type_ = Type::Value;
-	obj.value_ = std::to_string(value);
-}
-
-template<>
-std::optional<std::string>
-YamlObject::Accessor<std::string>::get(const YamlObject &obj) const
-{
-	if (obj.type_ != Type::Value)
-		return std::nullopt;
-
-	return obj.value_;
-}
-
-template<>
-void YamlObject::Accessor<std::string>::set(YamlObject &obj, std::string value)
-{
-	if (obj.type_ != Type::Empty && obj.type_ != Type::Value)
-		return;
-
-	obj.type_ = Type::Value;
-	obj.value_ = std::move(value);
-}
-
-template<typename T>
-struct YamlObject::Accessor<std::vector<T>, std::enable_if_t<
-	std::is_same_v<bool, T> ||
-	std::is_same_v<float, T> ||
-	std::is_same_v<double, T> ||
-	std::is_same_v<int8_t, T> ||
-	std::is_same_v<uint8_t, T> ||
-	std::is_same_v<int16_t, T> ||
-	std::is_same_v<uint16_t, T> ||
-	std::is_same_v<int32_t, T> ||
-	std::is_same_v<uint32_t, T> ||
-	std::is_same_v<std::string, T>>>
-{
-	std::optional<std::vector<T>> get(const YamlObject &obj) const
-	{
-		if (obj.type_ != Type::List)
-			return std::nullopt;
-
-		std::vector<T> values;
-		values.reserve(obj.list_.size());
-
-		for (const YamlObject &entry : obj.asList()) {
-			const auto value = entry.get<T>();
-			if (!value)
-				return std::nullopt;
-			values.emplace_back(*value);
-		}
-
-		return values;
-	}
-};
-
-template struct YamlObject::Accessor<std::vector<bool>>;
-template struct YamlObject::Accessor<std::vector<float>>;
-template struct YamlObject::Accessor<std::vector<double>>;
-template struct YamlObject::Accessor<std::vector<int8_t>>;
-template struct YamlObject::Accessor<std::vector<uint8_t>>;
-template struct YamlObject::Accessor<std::vector<int16_t>>;
-template struct YamlObject::Accessor<std::vector<uint16_t>>;
-template struct YamlObject::Accessor<std::vector<int32_t>>;
-template struct YamlObject::Accessor<std::vector<uint32_t>>;
-template struct YamlObject::Accessor<std::vector<std::string>>;
-#endif /* __DOXYGEN__ */
-
-/**
- * \fn YamlObject::asDict() const
- * \brief Wrap a dictionary YamlObject in an adapter that exposes iterators
- *
- * The YamlObject class doesn't directly implement iterators, as the iterator
- * type depends on whether the object is a Dictionary or List. This function
- * wraps a YamlObject of Dictionary type into an adapter that exposes
- * iterators, as well as begin() and end() functions, allowing usage of
- * range-based for loops with YamlObject. As YAML mappings are not ordered, the
- * iteration order is not specified.
- *
- * The iterator's value_type is a
- * <em>std::pair<const std::string &, const \ref YamlObject &></em>.
- *
- * If the YamlObject is not of Dictionary type, the returned adapter operates
- * as an empty container.
- *
- * \return An adapter of unspecified type compatible with range-based for loops
- */
-
-/**
- * \fn YamlObject::asList() const
- * \brief Wrap a list YamlObject in an adapter that exposes iterators
- *
- * The YamlObject class doesn't directly implement iterators, as the iterator
- * type depends on whether the object is a Dictionary or List. This function
- * wraps a YamlObject of List type into an adapter that exposes iterators, as
- * well as begin() and end() functions, allowing usage of range-based for loops
- * with YamlObject. As YAML lists are ordered, the iteration order is identical
- * to the list order in the YAML data.
- *
- * The iterator's value_type is a <em>const YamlObject &</em>.
- *
- * If the YamlObject is not of List type, the returned adapter operates as an
- * empty container.
- *
- * \return An adapter of unspecified type compatible with range-based for loops
- */
-
-/**
- * \fn YamlObject::operator[](std::size_t index) const
- * \brief Retrieve the element from list YamlObject by index
- *
- * This function retrieves an element of the YamlObject. Only YamlObject
- * instances of List type associate elements with index, calling this function
- * on other types of instances or with an invalid index results in an empty
- * object.
- *
- * \return The YamlObject as an element of the list
- */
-const YamlObject &YamlObject::operator[](std::size_t index) const
-{
-	if (type_ != Type::List || index >= size())
-		return empty;
-
-	return *list_[index].value;
-}
-
-/**
- * \fn YamlObject::contains()
- * \brief Check if an element of a dictionary exists
- *
- * This function check if the YamlObject contains an element. Only YamlObject
- * instances of Dictionary type associate elements with names, calling this
- * function on other types of instances is invalid and results in undefined
- * behaviour.
- *
- * \return True if an element exists, false otherwise
- */
-bool YamlObject::contains(std::string_view key) const
-{
-	return dictionary_.find(key) != dictionary_.end();
-}
-
-/**
- * \fn YamlObject::operator[](std::string_view key) const
- * \brief Retrieve a member by name from the dictionary
- *
- * This function retrieve a member of a YamlObject by name. Only YamlObject
- * instances of Dictionary type associate elements with names, calling this
- * function on other types of instances or with a nonexistent key results in an
- * empty object.
- *
- * \return The YamlObject corresponding to the \a key member
- */
-const YamlObject &YamlObject::operator[](std::string_view key) const
-{
-	if (type_ != Type::Dictionary)
-		return empty;
-
-	auto iter = dictionary_.find(key);
-	if (iter == dictionary_.end())
-		return empty;
-
-	return *iter->second;
-}
-
-/**
- * \brief Add a child object to a list
- * \param[in] child The child object
- *
- * Append the \a child node as the last element of this node's children list.
- * This node must be empty, in which case it is converted to the Type::List
- * type, or be a list. Otherwise, the \a child is discarded and the function
- * returns a nullptr.
- *
- * \return A pointer to the child object if successfully added, nullptr
- * otherwise
- */
-YamlObject *YamlObject::add(std::unique_ptr<YamlObject> child)
-{
-	if (type_ == Type::Empty)
-		type_ = Type::List;
-
-	if (type_ != Type::List)
-		return nullptr;
-
-	Value &elem = list_.emplace_back(std::string{}, std::move(child));
-	return elem.value.get();
-}
-
-/**
- * \brief Add a child object to a dictionary
- * \param[in] key The dictionary key
- * \param[in] child The child object
- *
- * Add the \a child node with the given \a key to this node's children. This
- * node must be empty, in which case it is converted to the Type::Dictionary
- * type, or be a dictionary. Otherwise, the \a child is discarded and the
- * function returns a nullptr.
- *
- * Keys are unique. If a child with the same \a key already exist, the \a child
- * is discarded and the function returns a nullptr.
- *
- * \return A pointer to the child object if successfully added, nullptr
- * otherwise
- */
-YamlObject *YamlObject::add(std::string key, std::unique_ptr<YamlObject> child)
-{
-	if (type_ == Type::Empty)
-		type_ = Type::Dictionary;
-
-	if (type_ != Type::Dictionary)
-		return nullptr;
-
-	if (dictionary_.find(key) != dictionary_.end())
-		return nullptr;
-
-	Value &elem = list_.emplace_back(std::move(key), std::move(child));
-	dictionary_.emplace(elem.key, elem.value.get());
-	return elem.value.get();
-}
-
 #ifndef __DOXYGEN__
 
 class YamlParserContext