[v2,24/42] libcamera: value_node: Support adding nested children in one operation
diff mbox series

Message ID 20260407153427.1825999-25-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • libcamera: Global configuration file improvements
Related show

Commit Message

Laurent Pinchart April 7, 2026, 3:34 p.m. UTC
The GlobalConfiguration class will need to add nested children to a
ValueNode. Add a new overload to the add() function for this purpose.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes since v1:

- Documentation improvements
- Replace NULL with nullptr
- Don't move child node if add fails
---
 include/libcamera/internal/value_node.h |  3 ++
 src/libcamera/value_node.cpp            | 59 +++++++++++++++++++++++++
 2 files changed, 62 insertions(+)

Comments

Laurent Pinchart April 23, 2026, 9:22 p.m. UTC | #1
On Thu, Apr 23, 2026 at 03:59:48PM +0200, Barnabás Pőcze wrote:
> 2026. 04. 07. 17:34 keltezéssel, Laurent Pinchart írta:
> > The GlobalConfiguration class will need to add nested children to a
> > ValueNode. Add a new overload to the add() function for this purpose.
> > 
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> > Changes since v1:
> > 
> > - Documentation improvements
> > - Replace NULL with nullptr
> > - Don't move child node if add fails
> > ---
> >   include/libcamera/internal/value_node.h |  3 ++
> >   src/libcamera/value_node.cpp            | 59 +++++++++++++++++++++++++
> >   2 files changed, 62 insertions(+)
> > 
> > diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h
> > index d237b2c1e0d6..be28c2b73832 100644
> > --- a/include/libcamera/internal/value_node.h
> > +++ b/include/libcamera/internal/value_node.h
> > @@ -8,6 +8,7 @@
> >   
> >   #pragma once
> >   
> > +#include <initializer_list>
> >   #include <iterator>
> >   #include <map>
> >   #include <memory>
> > @@ -239,6 +240,8 @@ public:
> >   
> >   	ValueNode *add(std::unique_ptr<ValueNode> &&child);
> >   	ValueNode *add(std::string key, std::unique_ptr<ValueNode> &&child);
> > +	ValueNode *add(std::initializer_list<std::string_view> path,
> > +		       std::unique_ptr<ValueNode> &&child);
> >   
> >   private:
> >   	LIBCAMERA_DISABLE_COPY_AND_MOVE(ValueNode)
> > diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp
> > index 4c3a5e4d381d..e8db7ef3c37f 100644
> > --- a/src/libcamera/value_node.cpp
> > +++ b/src/libcamera/value_node.cpp
> > @@ -13,6 +13,8 @@
> >   #include <string>
> >   #include <vector>
> >   
> > +#include <libcamera/base/log.h>
> > +#include <libcamera/base/span.h>
> >   #include <libcamera/base/utils.h>
> >   
> >   /**
> > @@ -22,6 +24,8 @@
> >   
> >   namespace libcamera {
> >   
> > +LOG_DEFINE_CATEGORY(ValueNode)
> > +
> >   namespace {
> >   
> >   /* Empty static ValueNode as a safe result for invalid operations */
> > @@ -539,4 +543,59 @@ ValueNode *ValueNode::add(std::string key, std::unique_ptr<ValueNode> &&child)
> >   	return list_.emplace_back(it->first, std::move(child)).value.get();
> >   }
> >   
> > +/**
> > + * \brief Add a child node at the given path
> > + * \param[in] path The path
> > + * \param[in] child The child node
> > + *
> > + * Add the \a child node at the given \a path starting at this node. Missing
> > + * nodes are created along the path. Nodes along the path must be empty (in
> > + * which case they are converted to the Type::Dictionary type), be a dictionary,
> > + * or be missing. Otherwise, the function returns a nullptr and the \a child is
> > + * not modified.
> > + *
> > + * Path elements are unique in the context of a parent node. If a child with the
> > + * same \a key already exist at the end of the path, the function returns a
> > + * nullptr and the \a child is not modified.
> > + *
> > + * \note Any node added along the \a path will remain even if this function
> > + * returns a failure.
> > + *
> > + * \return A pointer to the \a child node if successfully added, nullptr
> > + * otherwise
> > + */
> > +ValueNode *ValueNode::add(std::initializer_list<std::string_view> path,
> > +			  std::unique_ptr<ValueNode> &&child)
> > +{
> > +	if (!path.size())
> > +		return nullptr;
> > +
> > +	ValueNode *node = this;
> > +
> > +	for (const auto [i, name] : utils::enumerate(path)) {
> > +		auto iter = node->dictionary_.find(name);
> > +		if (iter == node->dictionary_.end()) {
> > +			std::unique_ptr<ValueNode> obj;
> > +
> > +			if (i < path.size() - 1)
> > +				obj = std::make_unique<ValueNode>();
> > +			else
> > +				obj = std::move(child);
> 
> This is not in sync with the description because it modifies
> `child` even if the later `node->add()` fails. Maybe:

Oops, good catch.

> diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp
> index 8a1af13a5..7e021744c 100644
> --- a/src/libcamera/value_node.cpp
> +++ b/src/libcamera/value_node.cpp
> @@ -599,13 +599,10 @@ ValueNode *ValueNode::add(std::initializer_list<std::string_view> path,
>                  auto iter = node->dictionary_.find(name);
>                  if (iter == node->dictionary_.end()) {
>                          std::unique_ptr<ValueNode> obj;
> -
>                          if (i < path.size() - 1)
>                                  obj = std::make_unique<ValueNode>();
> -                       else
> -                               obj = std::move(child);
>   
> -                       node = node->add(std::string{ name }, std::move(obj));
> +                       node = node->add(std::string{ name }, obj ? std::move(obj) : std::move(child));

I'll do that.

>                          if (!node) {
>                                  Span<const std::string_view> pathName{ std::data(path), i + 1 };
>                                  LOG(ValueNode, Error)
> 
> 
> > +
> > +			node = node->add(std::string{ name }, std::move(obj));
> > +			if (!node) {
> > +				Span<const std::string_view> pathName{ std::data(path), i + 1 };
> > +				LOG(ValueNode, Error)
> > +					<< "Failed to populate '"
> > +					<< utils::join(pathName, "/") << "'";
> > +				return nullptr;
> > +			}
> > +		} else {
> > +			node = iter->second;
> > +		}
> > +	}
> > +
> > +	return node;
> > +}
> > +
> >   } /* namespace libcamera */

Patch
diff mbox series

diff --git a/include/libcamera/internal/value_node.h b/include/libcamera/internal/value_node.h
index d237b2c1e0d6..be28c2b73832 100644
--- a/include/libcamera/internal/value_node.h
+++ b/include/libcamera/internal/value_node.h
@@ -8,6 +8,7 @@ 
 
 #pragma once
 
+#include <initializer_list>
 #include <iterator>
 #include <map>
 #include <memory>
@@ -239,6 +240,8 @@  public:
 
 	ValueNode *add(std::unique_ptr<ValueNode> &&child);
 	ValueNode *add(std::string key, std::unique_ptr<ValueNode> &&child);
+	ValueNode *add(std::initializer_list<std::string_view> path,
+		       std::unique_ptr<ValueNode> &&child);
 
 private:
 	LIBCAMERA_DISABLE_COPY_AND_MOVE(ValueNode)
diff --git a/src/libcamera/value_node.cpp b/src/libcamera/value_node.cpp
index 4c3a5e4d381d..e8db7ef3c37f 100644
--- a/src/libcamera/value_node.cpp
+++ b/src/libcamera/value_node.cpp
@@ -13,6 +13,8 @@ 
 #include <string>
 #include <vector>
 
+#include <libcamera/base/log.h>
+#include <libcamera/base/span.h>
 #include <libcamera/base/utils.h>
 
 /**
@@ -22,6 +24,8 @@ 
 
 namespace libcamera {
 
+LOG_DEFINE_CATEGORY(ValueNode)
+
 namespace {
 
 /* Empty static ValueNode as a safe result for invalid operations */
@@ -539,4 +543,59 @@  ValueNode *ValueNode::add(std::string key, std::unique_ptr<ValueNode> &&child)
 	return list_.emplace_back(it->first, std::move(child)).value.get();
 }
 
+/**
+ * \brief Add a child node at the given path
+ * \param[in] path The path
+ * \param[in] child The child node
+ *
+ * Add the \a child node at the given \a path starting at this node. Missing
+ * nodes are created along the path. Nodes along the path must be empty (in
+ * which case they are converted to the Type::Dictionary type), be a dictionary,
+ * or be missing. Otherwise, the function returns a nullptr and the \a child is
+ * not modified.
+ *
+ * Path elements are unique in the context of a parent node. If a child with the
+ * same \a key already exist at the end of the path, the function returns a
+ * nullptr and the \a child is not modified.
+ *
+ * \note Any node added along the \a path will remain even if this function
+ * returns a failure.
+ *
+ * \return A pointer to the \a child node if successfully added, nullptr
+ * otherwise
+ */
+ValueNode *ValueNode::add(std::initializer_list<std::string_view> path,
+			  std::unique_ptr<ValueNode> &&child)
+{
+	if (!path.size())
+		return nullptr;
+
+	ValueNode *node = this;
+
+	for (const auto [i, name] : utils::enumerate(path)) {
+		auto iter = node->dictionary_.find(name);
+		if (iter == node->dictionary_.end()) {
+			std::unique_ptr<ValueNode> obj;
+
+			if (i < path.size() - 1)
+				obj = std::make_unique<ValueNode>();
+			else
+				obj = std::move(child);
+
+			node = node->add(std::string{ name }, std::move(obj));
+			if (!node) {
+				Span<const std::string_view> pathName{ std::data(path), i + 1 };
+				LOG(ValueNode, Error)
+					<< "Failed to populate '"
+					<< utils::join(pathName, "/") << "'";
+				return nullptr;
+			}
+		} else {
+			node = iter->second;
+		}
+	}
+
+	return node;
+}
+
 } /* namespace libcamera */