[libcamera-devel] [PATCH 2/7] libcamera: yaml_parser: Add iterator API

Jacopo Mondi jacopo at jmondi.org
Fri Jun 17 15:45:05 CEST 2022


Hi Laurent,

On Thu, Jun 16, 2022 at 05:23:58PM +0300, Laurent Pinchart via libcamera-devel wrote:
> Allow using range-based for loops over YamlObject instances by
> implementing iterators. New YamlObject::DictAdapter and
> YamlObject::ListAdapter adapter classes are introduced to provide
> different iterators depending on the object type.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> ---
>  include/libcamera/internal/yaml_parser.h | 117 ++++++++++++++++++++++-
>  src/libcamera/yaml_parser.cpp            |  39 ++++++++
>  2 files changed, 154 insertions(+), 2 deletions(-)
>
> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
> index 29b7b218f0d9..8edd32354184 100644
> --- a/include/libcamera/internal/yaml_parser.h
> +++ b/include/libcamera/internal/yaml_parser.h
> @@ -7,6 +7,7 @@
>
>  #pragma once
>
> +#include <iterator>
>  #include <map>
>  #include <string>
>  #include <vector>
> @@ -22,7 +23,116 @@ class YamlParserContext;
>
>  class YamlObject
>  {
> +private:
> +	using DictContainer = std::map<std::string, std::unique_ptr<YamlObject>>;
> +	using ListContainer = std::vector<std::unique_ptr<YamlObject>>;
> +
>  public:
> +#ifndef __DOXYGEN__
> +	template<typename Container, typename Derived>
> +	class Iterator
> +	{
> +	public:
> +		using difference_type = std::ptrdiff_t;
> +		using iterator_category = std::bidirectional_iterator_tag;

Does it make any different the fact you only implement operator++ ?
Should this be just a forward_iterator ?

> +
> +		Iterator(typename Container::const_iterator it)
> +			: it_(it)
> +		{
> +		}
> +
> +		Derived &operator++()

Being it_ a ::const_iterator, should the returned type be a const too  ?

> +		{
> +			++it_;
> +			return *static_cast<Derived *>(this);

Weird, I was expecting this to be
			return *static_cast<Derived *>(it);

But I've surely missed something as the class clearly works :)

> +		}
> +
> +		Derived operator++(int)

Is the 'int' tag to allow random access ?

> +		{
> +			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:
> +		typename Container::const_iterator it_;
> +	};
> +
> +	template<typename Container, typename Iterator>
> +	class Adapter
> +	{
> +	public:
> +		Adapter(const Container &container)
> +			: container_(container)
> +		{
> +		}
> +
> +		Iterator begin() const
> +		{
> +			return Iterator{ container_.begin() };
> +		}
> +
> +		Iterator end() const
> +		{
> +			return Iterator{ container_.end() };
> +		}
> +
> +	protected:
> +		const Container &container_;
> +	};
> +
> +	class ListIterator : public Iterator<ListContainer, ListIterator>
> +	{
> +	public:
> +		using value_type = const YamlObject &;
> +		using pointer = const YamlObject *;
> +		using reference = value_type;
> +
> +		value_type operator*() const
> +		{
> +			return *it_->get();
> +		}
> +
> +		pointer operator->() const
> +		{
> +			return it_->get();
> +		}
> +	};
> +
> +	class DictIterator : public Iterator<DictContainer, 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_->first, *it_->second.get() };
> +		}
> +	};
> +
> +	class DictAdapter : public Adapter<DictContainer, DictIterator>
> +	{
> +	public:
> +		using key_type = std::string;
> +	};
> +
> +	class ListAdapter : public Adapter<ListContainer, ListIterator>
> +	{
> +	};
> +#endif /* __DOXYGEN__ */
> +
>  	YamlObject();
>  	~YamlObject();
>
> @@ -55,6 +165,9 @@ public:
>  #endif
>  	T get(const T &defaultValue, bool *ok = nullptr) const;
>
> +	DictAdapter asDict() const { return DictAdapter{ dictionary_ }; }
> +	ListAdapter asList() const { return ListAdapter{ list_ }; }

When I first thought about making this iterable I was expecting
simple wrappers around std::list iterator functions. That would not
allow to have it as dict or as list. I wonder if providing both
abstraction is necessary (my bad, I never played very much with this
class).

More questions than comments, so I would say the patch looks good (as
far as I understand it :p )

Thanks
   j

> +
>  	const YamlObject &operator[](std::size_t index) const;
>
>  	bool contains(const std::string &key) const;
> @@ -75,8 +188,8 @@ private:
>  	Type type_;
>
>  	std::string value_;
> -	std::vector<std::unique_ptr<YamlObject>> list_;
> -	std::map<const std::string, std::unique_ptr<YamlObject>> dictionary_;
> +	ListContainer list_;
> +	DictContainer dictionary_;
>  };
>
>  class YamlParser final
> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
> index 85f6694f5fde..4df7e5a33d47 100644
> --- a/src/libcamera/yaml_parser.cpp
> +++ b/src/libcamera/yaml_parser.cpp
> @@ -259,6 +259,45 @@ Size YamlObject::get(const Size &defaultValue, bool *ok) const
>
>  #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
> --
> Regards,
>
> Laurent Pinchart
>


More information about the libcamera-devel mailing list