[v1] libcamera: yaml_parser: Take string keys in `std::string_view`
diff mbox series

Message ID 20241001160329.725278-1-pobrn@protonmail.com
State Accepted
Commit e879a86979ab368cd3744bd82fdc7c3013685f0f
Headers show
Series
  • [v1] libcamera: yaml_parser: Take string keys in `std::string_view`
Related show

Commit Message

Barnabás Pőcze Oct. 1, 2024, 4:03 p.m. UTC
In many cases a static string literal is used as key. Thus
having the argument type be `const std::string&` is suboptimal
since an `std::string` object needs to be constructed before
the call.

C++17 introduced `std::string_view`, using which the call
can be done with less overhead, as the `std::string_view`
is non-owning and may be passed in registers entirely.

So make `YamlObject::{contains,operator[]}` take the string keys
in `std::string_view`s.

Unfortunately, that is not sufficient yet, because `std::map::find()`
takes an reference to `const key_type`, which would be `const std::string&`
in the case of `YamlParser`. However, with a transparent comparator
such as `std::less<>` `std::map::find()` is able to accept any
object as the argument, and it forwards it to the comparator.

So make `YamlParser::dictionary_` use `std::less<>` as the comparator
to enable the use of `std::map::find()` with any type of argument.

Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
---
 include/libcamera/internal/yaml_parser.h |  7 ++++---
 src/libcamera/yaml_parser.cpp            | 11 ++++-------
 2 files changed, 8 insertions(+), 10 deletions(-)

Comments

Kieran Bingham Oct. 2, 2024, 9:53 p.m. UTC | #1
Quoting Barnabás Pőcze (2024-10-01 17:03:32)
> In many cases a static string literal is used as key. Thus
> having the argument type be `const std::string&` is suboptimal
> since an `std::string` object needs to be constructed before
> the call.
> 
> C++17 introduced `std::string_view`, using which the call
> can be done with less overhead, as the `std::string_view`
> is non-owning and may be passed in registers entirely.
> 
> So make `YamlObject::{contains,operator[]}` take the string keys
> in `std::string_view`s.
> 
> Unfortunately, that is not sufficient yet, because `std::map::find()`
> takes an reference to `const key_type`, which would be `const std::string&`
> in the case of `YamlParser`. However, with a transparent comparator
> such as `std::less<>` `std::map::find()` is able to accept any
> object as the argument, and it forwards it to the comparator.
> 
> So make `YamlParser::dictionary_` use `std::less<>` as the comparator
> to enable the use of `std::map::find()` with any type of argument.
> 
> Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>

This passes the CI and unit tests at
https://gitlab.freedesktop.org/camera/libcamera/-/pipelines/1281981
which includes tests covering the yaml-parser.

I don't think I would have spotted this ... but it looks and sounds
reasonable to me ...

Makes me wonder if sometimes we should just use a raw 'const char *'
(</me ducks>) ;-) but I think this gives us that with type/length safety
on top so seems like a win to me.

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

> ---
>  include/libcamera/internal/yaml_parser.h |  7 ++++---
>  src/libcamera/yaml_parser.cpp            | 11 ++++-------
>  2 files changed, 8 insertions(+), 10 deletions(-)
> 
> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
> index 6211ff4a..de452844 100644
> --- a/include/libcamera/internal/yaml_parser.h
> +++ b/include/libcamera/internal/yaml_parser.h
> @@ -12,6 +12,7 @@
>  #include <optional>
>  #include <stdint.h>
>  #include <string>
> +#include <string_view>
>  #include <vector>
>  
>  #include <libcamera/base/class.h>
> @@ -206,8 +207,8 @@ public:
>  
>         const YamlObject &operator[](std::size_t index) const;
>  
> -       bool contains(const std::string &key) const;
> -       const YamlObject &operator[](const std::string &key) const;
> +       bool contains(std::string_view key) const;
> +       const YamlObject &operator[](std::string_view key) const;
>  
>  private:
>         LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
> @@ -232,7 +233,7 @@ private:
>  
>         std::string value_;
>         Container list_;
> -       std::map<std::string, YamlObject *> dictionary_;
> +       std::map<std::string, YamlObject *, std::less<>> dictionary_;
>  };
>  
>  class YamlParser final
> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
> index 4784f2dc..7c0b341a 100644
> --- a/src/libcamera/yaml_parser.cpp
> +++ b/src/libcamera/yaml_parser.cpp
> @@ -481,16 +481,13 @@ const YamlObject &YamlObject::operator[](std::size_t index) const
>   *
>   * \return True if an element exists, false otherwise
>   */
> -bool YamlObject::contains(const std::string &key) const
> +bool YamlObject::contains(std::string_view key) const
>  {
> -       if (dictionary_.find(std::ref(key)) == dictionary_.end())
> -               return false;
> -
> -       return true;
> +       return dictionary_.find(key) != dictionary_.end();
>  }
>  
>  /**
> - * \fn YamlObject::operator[](const std::string &key) const
> + * \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
> @@ -500,7 +497,7 @@ bool YamlObject::contains(const std::string &key) const
>   *
>   * \return The YamlObject corresponding to the \a key member
>   */
> -const YamlObject &YamlObject::operator[](const std::string &key) const
> +const YamlObject &YamlObject::operator[](std::string_view key) const
>  {
>         if (type_ != Type::Dictionary)
>                 return empty;
> -- 
> 2.46.2
> 
>
Barnabás Pőcze Oct. 2, 2024, 10:26 p.m. UTC | #2
> Quoting Barnabás Pőcze (2024-10-01 17:03:32)
> > In many cases a static string literal is used as key. Thus
> > having the argument type be `const std::string&` is suboptimal
> > since an `std::string` object needs to be constructed before
> > the call.
> >
> > C++17 introduced `std::string_view`, using which the call
> > can be done with less overhead, as the `std::string_view`
> > is non-owning and may be passed in registers entirely.
> >
> > So make `YamlObject::{contains,operator[]}` take the string keys
> > in `std::string_view`s.
> >
> > Unfortunately, that is not sufficient yet, because `std::map::find()`
> > takes an reference to `const key_type`, which would be `const std::string&`
> > in the case of `YamlParser`. However, with a transparent comparator
> > such as `std::less<>` `std::map::find()` is able to accept any
> > object as the argument, and it forwards it to the comparator.
> >
> > So make `YamlParser::dictionary_` use `std::less<>` as the comparator
> > to enable the use of `std::map::find()` with any type of argument.
> >
> > Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
> 
> This passes the CI and unit tests at
> https://gitlab.freedesktop.org/camera/libcamera/-/pipelines/1281981
> which includes tests covering the yaml-parser.
> 
> I don't think I would have spotted this ... but it looks and sounds
> reasonable to me ...
> 
> Makes me wonder if sometimes we should just use a raw 'const char *'
> (</me ducks>) ;-) but I think this gives us that with type/length safety
> on top so seems like a win to me.

I think `const std::string&` is generally not the ideal choice for the type of a
function argument. I believe `std::string_view` is preferable, even `const char *`,
when NUL termination is needed because one wants to interface with such APIs.
If both work, then `std::string_view` has the advantage of being more flexible
while avoiding the need to call `strlen()` left and right.


Regards,
Barnabás Pőcze


> 
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> > ---
> >  include/libcamera/internal/yaml_parser.h |  7 ++++---
> >  src/libcamera/yaml_parser.cpp            | 11 ++++-------
> >  2 files changed, 8 insertions(+), 10 deletions(-)
> >
> > diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
> > index 6211ff4a..de452844 100644
> > --- a/include/libcamera/internal/yaml_parser.h
> > +++ b/include/libcamera/internal/yaml_parser.h
> > @@ -12,6 +12,7 @@
> >  #include <optional>
> >  #include <stdint.h>
> >  #include <string>
> > +#include <string_view>
> >  #include <vector>
> >
> >  #include <libcamera/base/class.h>
> > @@ -206,8 +207,8 @@ public:
> >
> >         const YamlObject &operator[](std::size_t index) const;
> >
> > -       bool contains(const std::string &key) const;
> > -       const YamlObject &operator[](const std::string &key) const;
> > +       bool contains(std::string_view key) const;
> > +       const YamlObject &operator[](std::string_view key) const;
> >
> >  private:
> >         LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
> > @@ -232,7 +233,7 @@ private:
> >
> >         std::string value_;
> >         Container list_;
> > -       std::map<std::string, YamlObject *> dictionary_;
> > +       std::map<std::string, YamlObject *, std::less<>> dictionary_;
> >  };
> >
> >  class YamlParser final
> > diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
> > index 4784f2dc..7c0b341a 100644
> > --- a/src/libcamera/yaml_parser.cpp
> > +++ b/src/libcamera/yaml_parser.cpp
> > @@ -481,16 +481,13 @@ const YamlObject &YamlObject::operator[](std::size_t index) const
> >   *
> >   * \return True if an element exists, false otherwise
> >   */
> > -bool YamlObject::contains(const std::string &key) const
> > +bool YamlObject::contains(std::string_view key) const
> >  {
> > -       if (dictionary_.find(std::ref(key)) == dictionary_.end())
> > -               return false;
> > -
> > -       return true;
> > +       return dictionary_.find(key) != dictionary_.end();
> >  }
> >
> >  /**
> > - * \fn YamlObject::operator[](const std::string &key) const
> > + * \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
> > @@ -500,7 +497,7 @@ bool YamlObject::contains(const std::string &key) const
> >   *
> >   * \return The YamlObject corresponding to the \a key member
> >   */
> > -const YamlObject &YamlObject::operator[](const std::string &key) const
> > +const YamlObject &YamlObject::operator[](std::string_view key) const
> >  {
> >         if (type_ != Type::Dictionary)
> >                 return empty;
> > --
> > 2.46.2
> >
> >
>
Laurent Pinchart Oct. 2, 2024, 10:34 p.m. UTC | #3
On Wed, Oct 02, 2024 at 10:53:02PM +0100, Kieran Bingham wrote:
> Quoting Barnabás Pőcze (2024-10-01 17:03:32)
> > In many cases a static string literal is used as key. Thus
> > having the argument type be `const std::string&` is suboptimal
> > since an `std::string` object needs to be constructed before
> > the call.
> > 
> > C++17 introduced `std::string_view`, using which the call
> > can be done with less overhead, as the `std::string_view`
> > is non-owning and may be passed in registers entirely.
> > 
> > So make `YamlObject::{contains,operator[]}` take the string keys
> > in `std::string_view`s.
> > 
> > Unfortunately, that is not sufficient yet, because `std::map::find()`
> > takes an reference to `const key_type`, which would be `const std::string&`
> > in the case of `YamlParser`. However, with a transparent comparator
> > such as `std::less<>` `std::map::find()` is able to accept any
> > object as the argument, and it forwards it to the comparator.
> > 
> > So make `YamlParser::dictionary_` use `std::less<>` as the comparator
> > to enable the use of `std::map::find()` with any type of argument.
> > 
> > Signed-off-by: Barnabás Pőcze <pobrn@protonmail.com>
> 
> This passes the CI and unit tests at
> https://gitlab.freedesktop.org/camera/libcamera/-/pipelines/1281981
> which includes tests covering the yaml-parser.
> 
> I don't think I would have spotted this ... but it looks and sounds
> reasonable to me ...
> 
> Makes me wonder if sometimes we should just use a raw 'const char *'
> (</me ducks>) ;-) but I think this gives us that with type/length safety
> on top so seems like a win to me.

std::string_view is essentially a const char * and a size_t. It's like a
span, but with an API optimized for usage with strings. I would have
sworn it was a C++20 feature, but it looks like I was wrong. We should
use it more frequently, for cases where callers have a const char *
(possibly mixed with std::string) and the callee needs the length of the
string. There an overhead when construction a std::string_view from a
const char * as the length needs to be determined, so we shouldn't use
it when the callee doesn't need the length of the string.

> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> > ---
> >  include/libcamera/internal/yaml_parser.h |  7 ++++---
> >  src/libcamera/yaml_parser.cpp            | 11 ++++-------
> >  2 files changed, 8 insertions(+), 10 deletions(-)
> > 
> > diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
> > index 6211ff4a..de452844 100644
> > --- a/include/libcamera/internal/yaml_parser.h
> > +++ b/include/libcamera/internal/yaml_parser.h
> > @@ -12,6 +12,7 @@
> >  #include <optional>
> >  #include <stdint.h>
> >  #include <string>
> > +#include <string_view>
> >  #include <vector>
> >  
> >  #include <libcamera/base/class.h>
> > @@ -206,8 +207,8 @@ public:
> >  
> >         const YamlObject &operator[](std::size_t index) const;
> >  
> > -       bool contains(const std::string &key) const;
> > -       const YamlObject &operator[](const std::string &key) const;
> > +       bool contains(std::string_view key) const;
> > +       const YamlObject &operator[](std::string_view key) const;
> >  
> >  private:
> >         LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
> > @@ -232,7 +233,7 @@ private:
> >  
> >         std::string value_;
> >         Container list_;
> > -       std::map<std::string, YamlObject *> dictionary_;
> > +       std::map<std::string, YamlObject *, std::less<>> dictionary_;
> >  };
> >  
> >  class YamlParser final
> > diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
> > index 4784f2dc..7c0b341a 100644
> > --- a/src/libcamera/yaml_parser.cpp
> > +++ b/src/libcamera/yaml_parser.cpp
> > @@ -481,16 +481,13 @@ const YamlObject &YamlObject::operator[](std::size_t index) const
> >   *
> >   * \return True if an element exists, false otherwise
> >   */
> > -bool YamlObject::contains(const std::string &key) const
> > +bool YamlObject::contains(std::string_view key) const
> >  {
> > -       if (dictionary_.find(std::ref(key)) == dictionary_.end())
> > -               return false;
> > -
> > -       return true;
> > +       return dictionary_.find(key) != dictionary_.end();
> >  }
> >  
> >  /**
> > - * \fn YamlObject::operator[](const std::string &key) const
> > + * \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
> > @@ -500,7 +497,7 @@ bool YamlObject::contains(const std::string &key) const
> >   *
> >   * \return The YamlObject corresponding to the \a key member
> >   */
> > -const YamlObject &YamlObject::operator[](const std::string &key) const
> > +const YamlObject &YamlObject::operator[](std::string_view key) const
> >  {
> >         if (type_ != Type::Dictionary)
> >                 return empty;

Patch
diff mbox series

diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h
index 6211ff4a..de452844 100644
--- a/include/libcamera/internal/yaml_parser.h
+++ b/include/libcamera/internal/yaml_parser.h
@@ -12,6 +12,7 @@ 
 #include <optional>
 #include <stdint.h>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <libcamera/base/class.h>
@@ -206,8 +207,8 @@  public:
 
 	const YamlObject &operator[](std::size_t index) const;
 
-	bool contains(const std::string &key) const;
-	const YamlObject &operator[](const std::string &key) const;
+	bool contains(std::string_view key) const;
+	const YamlObject &operator[](std::string_view key) const;
 
 private:
 	LIBCAMERA_DISABLE_COPY_AND_MOVE(YamlObject)
@@ -232,7 +233,7 @@  private:
 
 	std::string value_;
 	Container list_;
-	std::map<std::string, YamlObject *> dictionary_;
+	std::map<std::string, YamlObject *, std::less<>> dictionary_;
 };
 
 class YamlParser final
diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp
index 4784f2dc..7c0b341a 100644
--- a/src/libcamera/yaml_parser.cpp
+++ b/src/libcamera/yaml_parser.cpp
@@ -481,16 +481,13 @@  const YamlObject &YamlObject::operator[](std::size_t index) const
  *
  * \return True if an element exists, false otherwise
  */
-bool YamlObject::contains(const std::string &key) const
+bool YamlObject::contains(std::string_view key) const
 {
-	if (dictionary_.find(std::ref(key)) == dictionary_.end())
-		return false;
-
-	return true;
+	return dictionary_.find(key) != dictionary_.end();
 }
 
 /**
- * \fn YamlObject::operator[](const std::string &key) const
+ * \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
@@ -500,7 +497,7 @@  bool YamlObject::contains(const std::string &key) const
  *
  * \return The YamlObject corresponding to the \a key member
  */
-const YamlObject &YamlObject::operator[](const std::string &key) const
+const YamlObject &YamlObject::operator[](std::string_view key) const
 {
 	if (type_ != Type::Dictionary)
 		return empty;