[{"id":32142,"web_url":"https://patchwork.libcamera.org/comment/32142/","msgid":"<pxg7bnanm3xggwqxpwpmrhqnlhwtbsuzibqqzghaedaquuzlq2@chh43lucj3px>","date":"2024-11-13T11:39:01","subject":"Re: [PATCH] libcamera: yaml_parser: Use std::from_chars()","submitter":{"id":143,"url":"https://patchwork.libcamera.org/api/people/143/","name":"Jacopo Mondi","email":"jacopo.mondi@ideasonboard.com"},"content":"Hi Laurent\n\nOn Tue, Nov 12, 2024 at 08:56:37PM +0200, Laurent Pinchart wrote:\n> std::from_chars(), introduced in C++17, is a fast, locale-independent\n> string-to-arithmetic conversion function. The C++ standard library\n> provides overloads for all integer types, making it a prime candidate to\n> replace the manual handling of integer sizes in the YamlParser string to\n> integer conversion.\n>\n> Compared to std::strtol(), std::from_chars() doesn't recognize the '0x'\n\nonly if the base is 16, or do I read\nhttps://en.cppreference.com/w/cpp/utility/from_chars\nwrong\n\n> prefix or '+' prefix, and doesn't ignore leading white space. As the\n> YamlParser doesn't require those features, std::from_chars() can be used\n> safely, reducing the amount of code.\n\nOk, I presume we won't have \"0x..\" entries in config files ?\n\n>\n> C++17 also requires the standard C++ library to provide overloads for\n> floating-point types, but libc++ does not implement those. The float and\n\nDo you know why ?\n\n> bool implementations of YamlParser::Getter::get() are therefore kept\n> as-is.\n>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/internal/yaml_parser.h |   2 +-\n>  src/libcamera/yaml_parser.cpp            | 168 ++++-------------------\n>  2 files changed, 29 insertions(+), 141 deletions(-)\n>\n> diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> index de452844fe0a..8c7916565946 100644\n> --- a/include/libcamera/internal/yaml_parser.h\n> +++ b/include/libcamera/internal/yaml_parser.h\n> @@ -224,7 +224,7 @@ private:\n>  \t\tEmpty,\n>  \t};\n>\n> -\ttemplate<typename T>\n> +\ttemplate<typename T, typename Enable = void>\n>  \tstruct Getter {\n>  \t\tstd::optional<T> get(const YamlObject &obj) const;\n>  \t};\n> diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> index 7c0b341a32e7..db256ec5b04d 100644\n> --- a/src/libcamera/yaml_parser.cpp\n> +++ b/src/libcamera/yaml_parser.cpp\n> @@ -7,6 +7,7 @@\n>\n>  #include \"libcamera/internal/yaml_parser.h\"\n>\n> +#include <charconv>\n>  #include <cstdlib>\n>  #include <errno.h>\n>  #include <functional>\n> @@ -146,151 +147,38 @@ YamlObject::Getter<bool>::get(const YamlObject &obj) const\n>  \treturn std::nullopt;\n>  }\n>\n> -namespace {\n> -\n> -bool parseSignedInteger(const std::string &str, long min, long max,\n\nout-of-range conversion is now handled by std::from_chars(), nice\n\n> -\t\t\tlong *result)\n> +template<typename T>\n> +struct YamlObject::Getter<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> -\tif (str == \"\")\n> -\t\treturn false;\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> -\tchar *end;\n> +\t\tconst std::string &str = obj.value_;\n> +\t\tT value;\n>\n> -\terrno = 0;\n> -\tlong value = std::strtol(str.c_str(), &end, 10);\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> -\tif ('\\0' != *end || errno == ERANGE || value < min || value > max)\n> -\t\treturn false;\n> +\t\treturn value;\n> +\t}\n> +};\n>\n> -\t*result = value;\n> -\treturn true;\n> -}\n> -\n> -bool parseUnsignedInteger(const std::string &str, unsigned long max,\n> -\t\t\t  unsigned long *result)\n> -{\n> -\tif (str == \"\")\n> -\t\treturn false;\n> -\n> -\t/*\n> -\t * strtoul() accepts strings representing a negative number, in which\n> -\t * case it negates the converted value. We don't want to silently accept\n> -\t * negative values and return a large positive number, so check for a\n> -\t * minus sign (after optional whitespace) and return an error.\n> -\t */\n> -\tstd::size_t found = str.find_first_not_of(\" \\t\");\n> -\tif (found != std::string::npos && str[found] == '-')\n> -\t\treturn false;\n> -\n> -\tchar *end;\n> -\n> -\terrno = 0;\n> -\tunsigned long value = std::strtoul(str.c_str(), &end, 10);\n> -\n> -\tif ('\\0' != *end || errno == ERANGE || value > max)\n> -\t\treturn false;\n> -\n> -\t*result = value;\n> -\treturn true;\n> -}\n> -\n> -} /* namespace */\n> -\n> -template<>\n> -std::optional<int8_t>\n> -YamlObject::Getter<int8_t>::get(const YamlObject &obj) const\n> -{\n> -\tif (obj.type_ != Type::Value)\n> -\t\treturn std::nullopt;\n> -\n> -\tlong value;\n> -\n> -\tif (!parseSignedInteger(obj.value_, std::numeric_limits<int8_t>::min(),\n> -\t\t\t\tstd::numeric_limits<int8_t>::max(), &value))\n> -\t\treturn std::nullopt;\n> -\n> -\treturn value;\n> -}\n> -\n> -template<>\n> -std::optional<uint8_t>\n> -YamlObject::Getter<uint8_t>::get(const YamlObject &obj) const\n> -{\n> -\tif (obj.type_ != Type::Value)\n> -\t\treturn std::nullopt;\n> -\n> -\tunsigned long value;\n> -\n> -\tif (!parseUnsignedInteger(obj.value_, std::numeric_limits<uint8_t>::max(),\n> -\t\t\t\t  &value))\n> -\t\treturn std::nullopt;\n> -\n> -\treturn value;\n> -}\n> -\n> -template<>\n> -std::optional<int16_t>\n> -YamlObject::Getter<int16_t>::get(const YamlObject &obj) const\n> -{\n> -\tif (obj.type_ != Type::Value)\n> -\t\treturn std::nullopt;\n> -\n> -\tlong value;\n> -\n> -\tif (!parseSignedInteger(obj.value_, std::numeric_limits<int16_t>::min(),\n> -\t\t\t\tstd::numeric_limits<int16_t>::max(), &value))\n> -\t\treturn std::nullopt;\n> -\n> -\treturn value;\n> -}\n> -\n> -template<>\n> -std::optional<uint16_t>\n> -YamlObject::Getter<uint16_t>::get(const YamlObject &obj) const\n> -{\n> -\tif (obj.type_ != Type::Value)\n> -\t\treturn std::nullopt;\n> -\n> -\tunsigned long value;\n> -\n> -\tif (!parseUnsignedInteger(obj.value_, std::numeric_limits<uint16_t>::max(),\n> -\t\t\t\t  &value))\n> -\t\treturn std::nullopt;\n> -\n> -\treturn value;\n> -}\n> -\n> -template<>\n> -std::optional<int32_t>\n> -YamlObject::Getter<int32_t>::get(const YamlObject &obj) const\n> -{\n> -\tif (obj.type_ != Type::Value)\n> -\t\treturn std::nullopt;\n> -\n> -\tlong value;\n> -\n> -\tif (!parseSignedInteger(obj.value_, std::numeric_limits<int32_t>::min(),\n> -\t\t\t\tstd::numeric_limits<int32_t>::max(), &value))\n> -\t\treturn std::nullopt;\n> -\n> -\treturn value;\n> -}\n> -\n> -template<>\n> -std::optional<uint32_t>\n> -YamlObject::Getter<uint32_t>::get(const YamlObject &obj) const\n> -{\n> -\tif (obj.type_ != Type::Value)\n> -\t\treturn std::nullopt;\n> -\n> -\tunsigned long value;\n> -\n> -\tif (!parseUnsignedInteger(obj.value_, std::numeric_limits<uint32_t>::max(),\n> -\t\t\t\t  &value))\n> -\t\treturn std::nullopt;\n> -\n> -\treturn value;\n> -}\n> +template struct YamlObject::Getter<int8_t>;\n> +template struct YamlObject::Getter<uint8_t>;\n> +template struct YamlObject::Getter<int16_t>;\n> +template struct YamlObject::Getter<uint16_t>;\n> +template struct YamlObject::Getter<int32_t>;\n> +template struct YamlObject::Getter<uint32_t>;\n>\n>  template<>\n>  std::optional<float>\n>\n> base-commit: dcb90f13cf79d909d04dacf2cfbd256a4744347f\n\nLooks good to me\nReviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n\nThanks\n  j\n\n> --\n> Regards,\n>\n> Laurent Pinchart\n>","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 EA809BE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 13 Nov 2024 11:39:07 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1F61665809;\n\tWed, 13 Nov 2024 12:39:07 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id F3B32657CF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 13 Nov 2024 12:39:05 +0100 (CET)","from ideasonboard.com (net-93-150-226-204.cust.vodafonedsl.it\n\t[93.150.226.204])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 92F4B594;\n\tWed, 13 Nov 2024 12:38:52 +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=\"BSzfqaMO\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1731497932;\n\tbh=d7NmF4dANKMBJKMxltTdEzaV7Vaknjye4ANxUgWVz/M=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=BSzfqaMOGStBrgtnC7o7+2EsHu2/HVNtUh4j20GgvBL20bA9oaaECCTL0UejbD+7F\n\tL/YECXjmSez/1KSGI3DIntm6myrLu3yUKoDupAtpspqnx1s9hovPl+1FtGULQQtTAm\n\tGCyX9o5PDg9qKsTizLmbMqRHg7oF+DnB1KQrQpVs=","Date":"Wed, 13 Nov 2024 12:39:01 +0100","From":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH] libcamera: yaml_parser: Use std::from_chars()","Message-ID":"<pxg7bnanm3xggwqxpwpmrhqnlhwtbsuzibqqzghaedaquuzlq2@chh43lucj3px>","References":"<20241112185637.10232-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20241112185637.10232-1-laurent.pinchart@ideasonboard.com>","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>"}},{"id":32143,"web_url":"https://patchwork.libcamera.org/comment/32143/","msgid":"<20241113114312.GA14554@pendragon.ideasonboard.com>","date":"2024-11-13T11:43:12","subject":"Re: [PATCH] libcamera: yaml_parser: Use std::from_chars()","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Wed, Nov 13, 2024 at 12:39:01PM +0100, Jacopo Mondi wrote:\n> Hi Laurent\n> \n> On Tue, Nov 12, 2024 at 08:56:37PM +0200, Laurent Pinchart wrote:\n> > std::from_chars(), introduced in C++17, is a fast, locale-independent\n> > string-to-arithmetic conversion function. The C++ standard library\n> > provides overloads for all integer types, making it a prime candidate to\n> > replace the manual handling of integer sizes in the YamlParser string to\n> > integer conversion.\n> >\n> > Compared to std::strtol(), std::from_chars() doesn't recognize the '0x'\n> \n> only if the base is 16, or do I read\n> https://en.cppreference.com/w/cpp/utility/from_chars\n> wrong\n\nYou're correct. If the base is 10, there should be no '0x' prefix\nanyway.\n\n> > prefix or '+' prefix, and doesn't ignore leading white space. As the\n> > YamlParser doesn't require those features, std::from_chars() can be used\n> > safely, reducing the amount of code.\n> \n> Ok, I presume we won't have \"0x..\" entries in config files ?\n\nI wouldn't assume so, as we explicitly specify base 10 in the strtol()\ncall.\n\n> > C++17 also requires the standard C++ library to provide overloads for\n> > floating-point types, but libc++ does not implement those. The float and\n> \n> Do you know why ?\n\nI believe it's more complicated than it may seem. The C++ standard\nrequires that the roundtrip std::from_chars(std::to_chars(value))\nproduces the exact same value, and apparently it's not trivial.\n\n> > bool implementations of YamlParser::Getter::get() are therefore kept\n> > as-is.\n> >\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/internal/yaml_parser.h |   2 +-\n> >  src/libcamera/yaml_parser.cpp            | 168 ++++-------------------\n> >  2 files changed, 29 insertions(+), 141 deletions(-)\n> >\n> > diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> > index de452844fe0a..8c7916565946 100644\n> > --- a/include/libcamera/internal/yaml_parser.h\n> > +++ b/include/libcamera/internal/yaml_parser.h\n> > @@ -224,7 +224,7 @@ private:\n> >  \t\tEmpty,\n> >  \t};\n> >\n> > -\ttemplate<typename T>\n> > +\ttemplate<typename T, typename Enable = void>\n> >  \tstruct Getter {\n> >  \t\tstd::optional<T> get(const YamlObject &obj) const;\n> >  \t};\n> > diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> > index 7c0b341a32e7..db256ec5b04d 100644\n> > --- a/src/libcamera/yaml_parser.cpp\n> > +++ b/src/libcamera/yaml_parser.cpp\n> > @@ -7,6 +7,7 @@\n> >\n> >  #include \"libcamera/internal/yaml_parser.h\"\n> >\n> > +#include <charconv>\n> >  #include <cstdlib>\n> >  #include <errno.h>\n> >  #include <functional>\n> > @@ -146,151 +147,38 @@ YamlObject::Getter<bool>::get(const YamlObject &obj) const\n> >  \treturn std::nullopt;\n> >  }\n> >\n> > -namespace {\n> > -\n> > -bool parseSignedInteger(const std::string &str, long min, long max,\n> \n> out-of-range conversion is now handled by std::from_chars(), nice\n> \n> > -\t\t\tlong *result)\n> > +template<typename T>\n> > +struct YamlObject::Getter<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> > -\tif (str == \"\")\n> > -\t\treturn false;\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> > -\tchar *end;\n> > +\t\tconst std::string &str = obj.value_;\n> > +\t\tT value;\n> >\n> > -\terrno = 0;\n> > -\tlong value = std::strtol(str.c_str(), &end, 10);\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> > -\tif ('\\0' != *end || errno == ERANGE || value < min || value > max)\n> > -\t\treturn false;\n> > +\t\treturn value;\n> > +\t}\n> > +};\n> >\n> > -\t*result = value;\n> > -\treturn true;\n> > -}\n> > -\n> > -bool parseUnsignedInteger(const std::string &str, unsigned long max,\n> > -\t\t\t  unsigned long *result)\n> > -{\n> > -\tif (str == \"\")\n> > -\t\treturn false;\n> > -\n> > -\t/*\n> > -\t * strtoul() accepts strings representing a negative number, in which\n> > -\t * case it negates the converted value. We don't want to silently accept\n> > -\t * negative values and return a large positive number, so check for a\n> > -\t * minus sign (after optional whitespace) and return an error.\n> > -\t */\n> > -\tstd::size_t found = str.find_first_not_of(\" \\t\");\n> > -\tif (found != std::string::npos && str[found] == '-')\n> > -\t\treturn false;\n> > -\n> > -\tchar *end;\n> > -\n> > -\terrno = 0;\n> > -\tunsigned long value = std::strtoul(str.c_str(), &end, 10);\n> > -\n> > -\tif ('\\0' != *end || errno == ERANGE || value > max)\n> > -\t\treturn false;\n> > -\n> > -\t*result = value;\n> > -\treturn true;\n> > -}\n> > -\n> > -} /* namespace */\n> > -\n> > -template<>\n> > -std::optional<int8_t>\n> > -YamlObject::Getter<int8_t>::get(const YamlObject &obj) const\n> > -{\n> > -\tif (obj.type_ != Type::Value)\n> > -\t\treturn std::nullopt;\n> > -\n> > -\tlong value;\n> > -\n> > -\tif (!parseSignedInteger(obj.value_, std::numeric_limits<int8_t>::min(),\n> > -\t\t\t\tstd::numeric_limits<int8_t>::max(), &value))\n> > -\t\treturn std::nullopt;\n> > -\n> > -\treturn value;\n> > -}\n> > -\n> > -template<>\n> > -std::optional<uint8_t>\n> > -YamlObject::Getter<uint8_t>::get(const YamlObject &obj) const\n> > -{\n> > -\tif (obj.type_ != Type::Value)\n> > -\t\treturn std::nullopt;\n> > -\n> > -\tunsigned long value;\n> > -\n> > -\tif (!parseUnsignedInteger(obj.value_, std::numeric_limits<uint8_t>::max(),\n> > -\t\t\t\t  &value))\n> > -\t\treturn std::nullopt;\n> > -\n> > -\treturn value;\n> > -}\n> > -\n> > -template<>\n> > -std::optional<int16_t>\n> > -YamlObject::Getter<int16_t>::get(const YamlObject &obj) const\n> > -{\n> > -\tif (obj.type_ != Type::Value)\n> > -\t\treturn std::nullopt;\n> > -\n> > -\tlong value;\n> > -\n> > -\tif (!parseSignedInteger(obj.value_, std::numeric_limits<int16_t>::min(),\n> > -\t\t\t\tstd::numeric_limits<int16_t>::max(), &value))\n> > -\t\treturn std::nullopt;\n> > -\n> > -\treturn value;\n> > -}\n> > -\n> > -template<>\n> > -std::optional<uint16_t>\n> > -YamlObject::Getter<uint16_t>::get(const YamlObject &obj) const\n> > -{\n> > -\tif (obj.type_ != Type::Value)\n> > -\t\treturn std::nullopt;\n> > -\n> > -\tunsigned long value;\n> > -\n> > -\tif (!parseUnsignedInteger(obj.value_, std::numeric_limits<uint16_t>::max(),\n> > -\t\t\t\t  &value))\n> > -\t\treturn std::nullopt;\n> > -\n> > -\treturn value;\n> > -}\n> > -\n> > -template<>\n> > -std::optional<int32_t>\n> > -YamlObject::Getter<int32_t>::get(const YamlObject &obj) const\n> > -{\n> > -\tif (obj.type_ != Type::Value)\n> > -\t\treturn std::nullopt;\n> > -\n> > -\tlong value;\n> > -\n> > -\tif (!parseSignedInteger(obj.value_, std::numeric_limits<int32_t>::min(),\n> > -\t\t\t\tstd::numeric_limits<int32_t>::max(), &value))\n> > -\t\treturn std::nullopt;\n> > -\n> > -\treturn value;\n> > -}\n> > -\n> > -template<>\n> > -std::optional<uint32_t>\n> > -YamlObject::Getter<uint32_t>::get(const YamlObject &obj) const\n> > -{\n> > -\tif (obj.type_ != Type::Value)\n> > -\t\treturn std::nullopt;\n> > -\n> > -\tunsigned long value;\n> > -\n> > -\tif (!parseUnsignedInteger(obj.value_, std::numeric_limits<uint32_t>::max(),\n> > -\t\t\t\t  &value))\n> > -\t\treturn std::nullopt;\n> > -\n> > -\treturn value;\n> > -}\n> > +template struct YamlObject::Getter<int8_t>;\n> > +template struct YamlObject::Getter<uint8_t>;\n> > +template struct YamlObject::Getter<int16_t>;\n> > +template struct YamlObject::Getter<uint16_t>;\n> > +template struct YamlObject::Getter<int32_t>;\n> > +template struct YamlObject::Getter<uint32_t>;\n> >\n> >  template<>\n> >  std::optional<float>\n> >\n> > base-commit: dcb90f13cf79d909d04dacf2cfbd256a4744347f\n> \n> Looks good to me\n> Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>","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 297BCC324C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 13 Nov 2024 11:43:24 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2A0CB65809;\n\tWed, 13 Nov 2024 12:43:23 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4F87A657CF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 13 Nov 2024 12:43:21 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id C3A66594;\n\tWed, 13 Nov 2024 12:43:07 +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=\"mPFxxoQH\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1731498188;\n\tbh=Jqoj0NuEj/qPa3OZEmv85GTYVOScxHoSywxf8OBQIFA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=mPFxxoQH1eJ69D+z84MXxmIHlUFYy5ApXTRHbx1eTP9tcB0boR6k2v7qEEXe/UpQl\n\t7FsEJYb/n1wr/PcNQtJLPo5VDv/k5IqY0DcYdNFq5IC3keCOu1KOo1+pmWyHz6yhz5\n\tGBAEb6JonbTItHfu4Gdfs0Pm3hAemaHJU1cKGoWo=","Date":"Wed, 13 Nov 2024 13:43:12 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH] libcamera: yaml_parser: Use std::from_chars()","Message-ID":"<20241113114312.GA14554@pendragon.ideasonboard.com>","References":"<20241112185637.10232-1-laurent.pinchart@ideasonboard.com>\n\t<pxg7bnanm3xggwqxpwpmrhqnlhwtbsuzibqqzghaedaquuzlq2@chh43lucj3px>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<pxg7bnanm3xggwqxpwpmrhqnlhwtbsuzibqqzghaedaquuzlq2@chh43lucj3px>","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>"}},{"id":32144,"web_url":"https://patchwork.libcamera.org/comment/32144/","msgid":"<173149858840.1566557.1177785461634725724@ping.linuxembedded.co.uk>","date":"2024-11-13T11:49:48","subject":"Re: [PATCH] libcamera: yaml_parser: Use std::from_chars()","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Laurent Pinchart (2024-11-13 11:43:12)\n> On Wed, Nov 13, 2024 at 12:39:01PM +0100, Jacopo Mondi wrote:\n> > Hi Laurent\n> > \n> > On Tue, Nov 12, 2024 at 08:56:37PM +0200, Laurent Pinchart wrote:\n> > > std::from_chars(), introduced in C++17, is a fast, locale-independent\n> > > string-to-arithmetic conversion function. The C++ standard library\n> > > provides overloads for all integer types, making it a prime candidate to\n> > > replace the manual handling of integer sizes in the YamlParser string to\n> > > integer conversion.\n> > >\n> > > Compared to std::strtol(), std::from_chars() doesn't recognize the '0x'\n> > \n> > only if the base is 16, or do I read\n> > https://en.cppreference.com/w/cpp/utility/from_chars\n> > wrong\n> \n> You're correct. If the base is 10, there should be no '0x' prefix\n> anyway.\n> \n> > > prefix or '+' prefix, and doesn't ignore leading white space. As the\n> > > YamlParser doesn't require those features, std::from_chars() can be used\n> > > safely, reducing the amount of code.\n> > \n> > Ok, I presume we won't have \"0x..\" entries in config files ?\n> \n> I wouldn't assume so, as we explicitly specify base 10 in the strtol()\n> call.\n> \n> > > C++17 also requires the standard C++ library to provide overloads for\n> > > floating-point types, but libc++ does not implement those. The float and\n> > \n> > Do you know why ?\n> \n> I believe it's more complicated than it may seem. The C++ standard\n> requires that the roundtrip std::from_chars(std::to_chars(value))\n> produces the exact same value, and apparently it's not trivial.\n> \n> > > bool implementations of YamlParser::Getter::get() are therefore kept\n> > > as-is.\n\nMy first thought reading this was ... does this mean we have to have\n'two implementations' of something, but the result below does seem to\nshow a worthwhile code reduction so :\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> > >\n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > >  include/libcamera/internal/yaml_parser.h |   2 +-\n> > >  src/libcamera/yaml_parser.cpp            | 168 ++++-------------------\n> > >  2 files changed, 29 insertions(+), 141 deletions(-)\n> > >\n> > > diff --git a/include/libcamera/internal/yaml_parser.h b/include/libcamera/internal/yaml_parser.h\n> > > index de452844fe0a..8c7916565946 100644\n> > > --- a/include/libcamera/internal/yaml_parser.h\n> > > +++ b/include/libcamera/internal/yaml_parser.h\n> > > @@ -224,7 +224,7 @@ private:\n> > >             Empty,\n> > >     };\n> > >\n> > > -   template<typename T>\n> > > +   template<typename T, typename Enable = void>\n> > >     struct Getter {\n> > >             std::optional<T> get(const YamlObject &obj) const;\n> > >     };\n> > > diff --git a/src/libcamera/yaml_parser.cpp b/src/libcamera/yaml_parser.cpp\n> > > index 7c0b341a32e7..db256ec5b04d 100644\n> > > --- a/src/libcamera/yaml_parser.cpp\n> > > +++ b/src/libcamera/yaml_parser.cpp\n> > > @@ -7,6 +7,7 @@\n> > >\n> > >  #include \"libcamera/internal/yaml_parser.h\"\n> > >\n> > > +#include <charconv>\n> > >  #include <cstdlib>\n> > >  #include <errno.h>\n> > >  #include <functional>\n> > > @@ -146,151 +147,38 @@ YamlObject::Getter<bool>::get(const YamlObject &obj) const\n> > >     return std::nullopt;\n> > >  }\n> > >\n> > > -namespace {\n> > > -\n> > > -bool parseSignedInteger(const std::string &str, long min, long max,\n> > \n> > out-of-range conversion is now handled by std::from_chars(), nice\n> > \n> > > -                   long *result)\n> > > +template<typename T>\n> > > +struct YamlObject::Getter<T, std::enable_if_t<\n> > > +   std::is_same_v<int8_t, T> ||\n> > > +   std::is_same_v<uint8_t, T> ||\n> > > +   std::is_same_v<int16_t, T> ||\n> > > +   std::is_same_v<uint16_t, T> ||\n> > > +   std::is_same_v<int32_t, T> ||\n> > > +   std::is_same_v<uint32_t, T>>>\n> > >  {\n> > > -   if (str == \"\")\n> > > -           return false;\n> > > +   std::optional<T> get(const YamlObject &obj) const\n> > > +   {\n> > > +           if (obj.type_ != Type::Value)\n> > > +                   return std::nullopt;\n> > >\n> > > -   char *end;\n> > > +           const std::string &str = obj.value_;\n> > > +           T value;\n> > >\n> > > -   errno = 0;\n> > > -   long value = std::strtol(str.c_str(), &end, 10);\n> > > +           auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(),\n> > > +                                            value);\n> > > +           if (ptr != str.data() + str.size() || ec != std::errc())\n> > > +                   return std::nullopt;\n> > >\n> > > -   if ('\\0' != *end || errno == ERANGE || value < min || value > max)\n> > > -           return false;\n> > > +           return value;\n> > > +   }\n> > > +};\n> > >\n> > > -   *result = value;\n> > > -   return true;\n> > > -}\n> > > -\n> > > -bool parseUnsignedInteger(const std::string &str, unsigned long max,\n> > > -                     unsigned long *result)\n> > > -{\n> > > -   if (str == \"\")\n> > > -           return false;\n> > > -\n> > > -   /*\n> > > -    * strtoul() accepts strings representing a negative number, in which\n> > > -    * case it negates the converted value. We don't want to silently accept\n> > > -    * negative values and return a large positive number, so check for a\n> > > -    * minus sign (after optional whitespace) and return an error.\n> > > -    */\n> > > -   std::size_t found = str.find_first_not_of(\" \\t\");\n> > > -   if (found != std::string::npos && str[found] == '-')\n> > > -           return false;\n> > > -\n> > > -   char *end;\n> > > -\n> > > -   errno = 0;\n> > > -   unsigned long value = std::strtoul(str.c_str(), &end, 10);\n> > > -\n> > > -   if ('\\0' != *end || errno == ERANGE || value > max)\n> > > -           return false;\n> > > -\n> > > -   *result = value;\n> > > -   return true;\n> > > -}\n> > > -\n> > > -} /* namespace */\n> > > -\n> > > -template<>\n> > > -std::optional<int8_t>\n> > > -YamlObject::Getter<int8_t>::get(const YamlObject &obj) const\n> > > -{\n> > > -   if (obj.type_ != Type::Value)\n> > > -           return std::nullopt;\n> > > -\n> > > -   long value;\n> > > -\n> > > -   if (!parseSignedInteger(obj.value_, std::numeric_limits<int8_t>::min(),\n> > > -                           std::numeric_limits<int8_t>::max(), &value))\n> > > -           return std::nullopt;\n> > > -\n> > > -   return value;\n> > > -}\n> > > -\n> > > -template<>\n> > > -std::optional<uint8_t>\n> > > -YamlObject::Getter<uint8_t>::get(const YamlObject &obj) const\n> > > -{\n> > > -   if (obj.type_ != Type::Value)\n> > > -           return std::nullopt;\n> > > -\n> > > -   unsigned long value;\n> > > -\n> > > -   if (!parseUnsignedInteger(obj.value_, std::numeric_limits<uint8_t>::max(),\n> > > -                             &value))\n> > > -           return std::nullopt;\n> > > -\n> > > -   return value;\n> > > -}\n> > > -\n> > > -template<>\n> > > -std::optional<int16_t>\n> > > -YamlObject::Getter<int16_t>::get(const YamlObject &obj) const\n> > > -{\n> > > -   if (obj.type_ != Type::Value)\n> > > -           return std::nullopt;\n> > > -\n> > > -   long value;\n> > > -\n> > > -   if (!parseSignedInteger(obj.value_, std::numeric_limits<int16_t>::min(),\n> > > -                           std::numeric_limits<int16_t>::max(), &value))\n> > > -           return std::nullopt;\n> > > -\n> > > -   return value;\n> > > -}\n> > > -\n> > > -template<>\n> > > -std::optional<uint16_t>\n> > > -YamlObject::Getter<uint16_t>::get(const YamlObject &obj) const\n> > > -{\n> > > -   if (obj.type_ != Type::Value)\n> > > -           return std::nullopt;\n> > > -\n> > > -   unsigned long value;\n> > > -\n> > > -   if (!parseUnsignedInteger(obj.value_, std::numeric_limits<uint16_t>::max(),\n> > > -                             &value))\n> > > -           return std::nullopt;\n> > > -\n> > > -   return value;\n> > > -}\n> > > -\n> > > -template<>\n> > > -std::optional<int32_t>\n> > > -YamlObject::Getter<int32_t>::get(const YamlObject &obj) const\n> > > -{\n> > > -   if (obj.type_ != Type::Value)\n> > > -           return std::nullopt;\n> > > -\n> > > -   long value;\n> > > -\n> > > -   if (!parseSignedInteger(obj.value_, std::numeric_limits<int32_t>::min(),\n> > > -                           std::numeric_limits<int32_t>::max(), &value))\n> > > -           return std::nullopt;\n> > > -\n> > > -   return value;\n> > > -}\n> > > -\n> > > -template<>\n> > > -std::optional<uint32_t>\n> > > -YamlObject::Getter<uint32_t>::get(const YamlObject &obj) const\n> > > -{\n> > > -   if (obj.type_ != Type::Value)\n> > > -           return std::nullopt;\n> > > -\n> > > -   unsigned long value;\n> > > -\n> > > -   if (!parseUnsignedInteger(obj.value_, std::numeric_limits<uint32_t>::max(),\n> > > -                             &value))\n> > > -           return std::nullopt;\n> > > -\n> > > -   return value;\n> > > -}\n> > > +template struct YamlObject::Getter<int8_t>;\n> > > +template struct YamlObject::Getter<uint8_t>;\n> > > +template struct YamlObject::Getter<int16_t>;\n> > > +template struct YamlObject::Getter<uint16_t>;\n> > > +template struct YamlObject::Getter<int32_t>;\n> > > +template struct YamlObject::Getter<uint32_t>;\n> > >\n> > >  template<>\n> > >  std::optional<float>\n> > >\n> > > base-commit: dcb90f13cf79d909d04dacf2cfbd256a4744347f\n> > \n> > Looks good to me\n> > Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart","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 71ECABE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 13 Nov 2024 11:49:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CC0E865811;\n\tWed, 13 Nov 2024 12:49:53 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C11E2657CF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 13 Nov 2024 12:49:51 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7EE54594;\n\tWed, 13 Nov 2024 12:49:38 +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=\"lPuRplmr\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1731498578;\n\tbh=Dw8ZNqbfqFyVeLoG1OJo0bbLExIz8mJ4Ek8ZZj9aW8o=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=lPuRplmr8cAP4pezJCFLMWcif2YLxKuA03zxJ9Wl0mzR+Yc9aKm/3j6FLUzWQ83fr\n\tcHk72nvwTILgfNPa9C9GnteZu2WkewL51ebj602+xNwLgZYSARPKTomxhZamloTWcO\n\tmT57J/dcYavcvWTwZlHRr2xtQcTnntsv31/ozrPU=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20241113114312.GA14554@pendragon.ideasonboard.com>","References":"<20241112185637.10232-1-laurent.pinchart@ideasonboard.com>\n\t<pxg7bnanm3xggwqxpwpmrhqnlhwtbsuzibqqzghaedaquuzlq2@chh43lucj3px>\n\t<20241113114312.GA14554@pendragon.ideasonboard.com>","Subject":"Re: [PATCH] libcamera: yaml_parser: Use std::from_chars()","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Wed, 13 Nov 2024 11:49:48 +0000","Message-ID":"<173149858840.1566557.1177785461634725724@ping.linuxembedded.co.uk>","User-Agent":"alot/0.10","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>"}}]