[{"id":22925,"web_url":"https://patchwork.libcamera.org/comment/22925/","msgid":"<ccc4c7a3-ddd4-7a39-9f9d-5b328b15f6b4@ideasonboard.com>","date":"2022-05-10T09:10:21","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":109,"url":"https://patchwork.libcamera.org/api/people/109/","name":"Tomi Valkeinen","email":"tomi.valkeinen@ideasonboard.com"},"content":"On 10/05/2022 10:16, Tomi Valkeinen wrote:\n> This is just a conversation starter, not for merging. I really like\n> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> is the first library I add to any new C++ project I make.\n> \n> You can find more information about libfmt from:\n> \n> https://github.com/fmtlib/fmt\n> https://fmt.dev/latest/index.html\n> \n> This patch is just a crude conversion with ugly macros to showcase what\n> the formatting code might look like.\n\nAs for the PR and EPR macros, those was just something to get forward \nwith. I think fmt::print(\"\") is fine, but fmt::print(stderr, \"\") is a \nbit long.\n\nCompared to cout, \"fmt::print(\" is actually shorter than \"std::cout << \n\", and without all those << and std::endl the lines are shorter.\n\nNot so with cerr and fmt::print(stderr, (although in many cases the \ntotal length would still be shorter) but I think it makes sense to have \na macro/inline func for error prints, if only to make it more obvious \nthat it's an error print.\n\nAnd, of course, libfmt could also be used for logging:\n\nLOG(Camera, Error) << \"Camera in \" << camera_state_names[currentState]\n\t\t   << \" state trying \" << from << \"() requiring state \"\n\t\t   << camera_state_names[state];\n\nto\n\nLOG(Camera, Error, \"Camera in {} state trying {}() requiring state {}\",\n     camera_state_names[currentState], from, camera_state_names[state]);\n\n  Tomi","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 E314BC0F2A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 09:10:26 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5058E65641;\n\tTue, 10 May 2022 11:10:26 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E624E6563E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 11:10:24 +0200 (CEST)","from [192.168.1.111] (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 4F15D55A;\n\tTue, 10 May 2022 11:10:24 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652173826;\n\tbh=FZODP/lLVo0mTVlO1UiwTrTkwxj8UNThp4AZoJZh+p8=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=J0E/Exp8CNvYovRI74FSwqnU4XeDzOGg9yFWqT76wEyRS8m5AeouT/4gLbW+H1Wc5\n\t7DrGmXa9AjPsBDA1kGnmNt3xFdIUruEtv4Bhg+A05a7lg6tUowLR+So7ChUftzCBDS\n\twoRhqXM7rx5dp5zV764UGyJnsgfaq+kon7xROx0Uu4AQTnJJFgBTOUrdE+whSed/Hb\n\tb9reBZHxsP8qmKG+ANRHSGcLwgLgYD3hFLZcHptUOXQtc78Q7GUbqKp6+uAaSKTnWm\n\te4sSFnlf1UIqfosa91jpVJJDsTdtsLxa+ocMs6+mXg4yFOTmM8EdVAcYyX11u0pmZs\n\t+Va8ix32AExCg==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652173824;\n\tbh=FZODP/lLVo0mTVlO1UiwTrTkwxj8UNThp4AZoJZh+p8=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=RyIr1WDtRu1UY66TkYXBnD+P1cMNWFTn+W5aNtGKEcuKfmOP+XWNVU2HZeNANrp8k\n\tqGj0gS/zAAyWKnPVMEWOyVmJ6f5BtdMzqaAIMN3bx73WqpDGZH7KfE2JceQj0QDsAa\n\tFBrJpY3JIBtgccQyIHVpcvvfWOJTv40+wO5eyVO0="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"RyIr1WDt\"; dkim-atps=neutral","Message-ID":"<ccc4c7a3-ddd4-7a39-9f9d-5b328b15f6b4@ideasonboard.com>","Date":"Tue, 10 May 2022 12:10:21 +0300","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.8.0","Content-Language":"en-US","To":"libcamera-devel@lists.libcamera.org,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>","In-Reply-To":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Tomi Valkeinen via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22929,"web_url":"https://patchwork.libcamera.org/comment/22929/","msgid":"<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>","date":"2022-05-10T09:49:14","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":34,"url":"https://patchwork.libcamera.org/api/people/34/","name":"Naushir Patuck","email":"naush@raspberrypi.com"},"content":"Hi Tomi,\n\nOn Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel <\nlibcamera-devel@lists.libcamera.org> wrote:\n\n> This is just a conversation starter, not for merging. I really like\n> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> is the first library I add to any new C++ project I make.\n>\n> You can find more information about libfmt from:\n>\n> https://github.com/fmtlib/fmt\n> https://fmt.dev/latest/index.html\n>\n> This patch is just a crude conversion with ugly macros to showcase what\n> the formatting code might look like.\n>\n> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> than iostreams, but for the size it didn't seem to be true in cam's case\n> as the tests below show. However, simple prints did reduce the exe size,\n> but the few more complex ones increased the size.\n>\n> Size tests with gcc 11.2.0-19ubuntu1\n>\n> - Without libfmt\n>\n> debug           3523400\n> debug lto       3269368\n> release         223056\n> release lto     172280\n>\n> - With libfmt\n>\n> debug           4424256\n> debug lto       4143840\n> release         303952\n> release lto     252640\n>\n> Above shows that cam's size clearly increases with libfmt. However, the\n> increase really comes only from one case, the use of fmt::memory_buffer\n> and std::back_inserter. Converting that code to use fmt::format() and\n> naive string append gives:\n>\n> release with string append      233680\n> release lto with string append  186936\n>\n> Also, if I add another use of fmt::memory_buffer and std::back_inserter\n> to another file, I see much less increase in the size:\n>\n> release lto with two uses of memory_buffer, back_inserter       256736\n>\n> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n>\n\nFor what it's worth, I absolutely loathe formatting in std iostream, and\nlibfmt is a wonderful alternative.\nIt also is close enough to the C++20 std::format implementation that\neventual porting would be low effort.  So I am all for this change :)\n\nRegards,\nNaush\n\n\n\n> ---\n>  src/cam/camera_session.cpp | 105 ++++++++++++++++---------------------\n>  src/cam/drm.cpp            |  68 ++++++++----------------\n>  src/cam/event_loop.cpp     |   9 ++--\n>  src/cam/file_sink.cpp      |  31 +++++------\n>  src/cam/image.cpp          |  15 +++---\n>  src/cam/kms_sink.cpp       |  38 ++++++--------\n>  src/cam/main.cpp           |  28 +++++-----\n>  src/cam/meson.build        |   5 +-\n>  src/cam/options.cpp        |  66 ++++++++++-------------\n>  src/cam/stream_options.cpp |  18 +++----\n>  10 files changed, 165 insertions(+), 218 deletions(-)\n>\n> diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp\n> index efffafbf..7843c3fd 100644\n> --- a/src/cam/camera_session.cpp\n> +++ b/src/cam/camera_session.cpp\n> @@ -5,10 +5,9 @@\n>   * camera_session.cpp - Camera capture session\n>   */\n>\n> -#include <iomanip>\n> -#include <iostream>\n>  #include <limits.h>\n> -#include <sstream>\n> +#include <fmt/format.h>\n> +#include <fmt/ostream.h>\n>\n>  #include <libcamera/control_ids.h>\n>  #include <libcamera/property_ids.h>\n> @@ -22,6 +21,9 @@\n>  #include \"main.h\"\n>  #include \"stream_options.h\"\n>\n> +#define PR(...) fmt::print(__VA_ARGS__)\n> +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> +\n>  using namespace libcamera;\n>\n>  CameraSession::CameraSession(CameraManager *cm,\n> @@ -40,13 +42,12 @@ CameraSession::CameraSession(CameraManager *cm,\n>                 camera_ = cm->get(cameraId);\n>\n>         if (!camera_) {\n> -               std::cerr << \"Camera \" << cameraId << \" not found\" <<\n> std::endl;\n> +               EPR(\"Camera {} not found\\n\", cameraId);\n>                 return;\n>         }\n>\n>         if (camera_->acquire()) {\n> -               std::cerr << \"Failed to acquire camera \" << cameraId\n> -                         << std::endl;\n> +               EPR(\"Failed to acquire camera {}\", cameraId);\n>                 return;\n>         }\n>\n> @@ -55,15 +56,14 @@ CameraSession::CameraSession(CameraManager *cm,\n>         std::unique_ptr<CameraConfiguration> config =\n>                 camera_->generateConfiguration(roles);\n>         if (!config || config->size() != roles.size()) {\n> -               std::cerr << \"Failed to get default stream configuration\"\n> -                         << std::endl;\n> +               EPR(\"Failed to get default stream configuration\\n\");\n>                 return;\n>         }\n>\n>         /* Apply configuration if explicitly requested. */\n>         if (StreamKeyValueParser::updateConfiguration(config.get(),\n>\n> options_[OptStream])) {\n> -               std::cerr << \"Failed to update configuration\" << std::endl;\n> +               EPR(\"Failed to update configuration\\n\");\n>                 return;\n>         }\n>\n> @@ -72,20 +72,17 @@ CameraSession::CameraSession(CameraManager *cm,\n>  #ifdef HAVE_KMS\n>         if (options_.isSet(OptDisplay)) {\n>                 if (options_.isSet(OptFile)) {\n> -                       std::cerr << \"--display and --file options are\n> mutually exclusive\"\n> -                                 << std::endl;\n> +                       EPR(\"--display and --file options are mutually\n> exclusive\\n\");\n>                         return;\n>                 }\n>\n>                 if (roles.size() != 1) {\n> -                       std::cerr << \"Display doesn't support multiple\n> streams\"\n> -                                 << std::endl;\n> +                       EPR(\"Display doesn't support multiple streams\\n\");\n>                         return;\n>                 }\n>\n>                 if (roles[0] != StreamRole::Viewfinder) {\n> -                       std::cerr << \"Display requires a viewfinder stream\"\n> -                                 << std::endl;\n> +                       EPR(\"Display requires a viewfinder stream\\n\");\n>                         return;\n>                 }\n>         }\n> @@ -97,15 +94,14 @@ CameraSession::CameraSession(CameraManager *cm,\n>\n>         case CameraConfiguration::Adjusted:\n>                 if (strictFormats) {\n> -                       std::cout << \"Adjusting camera configuration\n> disallowed by --strict-formats argument\"\n> -                                 << std::endl;\n> +                       PR(\"Adjusting camera configuration disallowed by\n> --strict-formats argument\\n\");\n>                         return;\n>                 }\n> -               std::cout << \"Camera configuration adjusted\" << std::endl;\n> +               PR(\"Camera configuration adjusted\\n\");\n>                 break;\n>\n>         case CameraConfiguration::Invalid:\n> -               std::cout << \"Camera configuration invalid\" << std::endl;\n> +               PR(\"Camera configuration invalid\\n\");\n>                 return;\n>         }\n>\n> @@ -121,8 +117,7 @@ CameraSession::~CameraSession()\n>  void CameraSession::listControls() const\n>  {\n>         for (const auto &[id, info] : camera_->controls()) {\n> -               std::cout << \"Control: \" << id->name() << \": \"\n> -                         << info.toString() << std::endl;\n> +               PR(\"Control: {}: {}}n\", id->name(), info.toString());\n>         }\n>  }\n>\n> @@ -131,8 +126,7 @@ void CameraSession::listProperties() const\n>         for (const auto &[key, value] : camera_->properties()) {\n>                 const ControlId *id = properties::properties.at(key);\n>\n> -               std::cout << \"Property: \" << id->name() << \" = \"\n> -                         << value.toString() << std::endl;\n> +               PR(\"Property: {} = {}\\n\", id->name(), value.toString());\n>         }\n>  }\n>\n> @@ -140,17 +134,15 @@ void CameraSession::infoConfiguration() const\n>  {\n>         unsigned int index = 0;\n>         for (const StreamConfiguration &cfg : *config_) {\n> -               std::cout << index << \": \" << cfg.toString() << std::endl;\n> +               PR(\"{}: {}\\n\", index, cfg.toString());\n>\n>                 const StreamFormats &formats = cfg.formats();\n>                 for (PixelFormat pixelformat : formats.pixelformats()) {\n> -                       std::cout << \" * Pixelformat: \"\n> -                                 << pixelformat << \" \"\n> -                                 << formats.range(pixelformat).toString()\n> -                                 << std::endl;\n> +                       PR(\" * Pixelformat: {} {}\\n\", pixelformat,\n> +                          formats.range(pixelformat).toString());\n>\n>                         for (const Size &size : formats.sizes(pixelformat))\n> -                               std::cout << \"  - \" << size << std::endl;\n> +                               PR(\"  - {}\\n\", size);\n>                 }\n>\n>                 index++;\n> @@ -168,7 +160,7 @@ int CameraSession::start()\n>\n>         ret = camera_->configure(config_.get());\n>         if (ret < 0) {\n> -               std::cout << \"Failed to configure camera\" << std::endl;\n> +               PR(\"Failed to configure camera\\n\");\n>                 return ret;\n>         }\n>\n> @@ -197,8 +189,7 @@ int CameraSession::start()\n>         if (sink_) {\n>                 ret = sink_->configure(*config_);\n>                 if (ret < 0) {\n> -                       std::cout << \"Failed to configure frame sink\"\n> -                                 << std::endl;\n> +                       PR(\"Failed to configure frame sink\\n\");\n>                         return ret;\n>                 }\n>\n> @@ -214,12 +205,12 @@ void CameraSession::stop()\n>  {\n>         int ret = camera_->stop();\n>         if (ret)\n> -               std::cout << \"Failed to stop capture\" << std::endl;\n> +               PR(\"Failed to stop capture\\n\");\n>\n>         if (sink_) {\n>                 ret = sink_->stop();\n>                 if (ret)\n> -                       std::cout << \"Failed to stop frame sink\" <<\n> std::endl;\n> +                       PR(\"Failed to stop frame sink\\n\");\n>         }\n>\n>         sink_.reset();\n> @@ -238,7 +229,7 @@ int CameraSession::startCapture()\n>         for (StreamConfiguration &cfg : *config_) {\n>                 ret = allocator_->allocate(cfg.stream());\n>                 if (ret < 0) {\n> -                       std::cerr << \"Can't allocate buffers\" << std::endl;\n> +                       EPR(\"Can't allocate buffers\\n\");\n>                         return -ENOMEM;\n>                 }\n>\n> @@ -254,7 +245,7 @@ int CameraSession::startCapture()\n>         for (unsigned int i = 0; i < nbuffers; i++) {\n>                 std::unique_ptr<Request> request =\n> camera_->createRequest();\n>                 if (!request) {\n> -                       std::cerr << \"Can't create request\" << std::endl;\n> +                       EPR(\"Can't create request\\n\");\n>                         return -ENOMEM;\n>                 }\n>\n> @@ -266,8 +257,7 @@ int CameraSession::startCapture()\n>\n>                         ret = request->addBuffer(stream, buffer.get());\n>                         if (ret < 0) {\n> -                               std::cerr << \"Can't set buffer for request\"\n> -                                         << std::endl;\n> +                               EPR(\"Can't set buffer for request\\n\");\n>                                 return ret;\n>                         }\n>\n> @@ -281,14 +271,14 @@ int CameraSession::startCapture()\n>         if (sink_) {\n>                 ret = sink_->start();\n>                 if (ret) {\n> -                       std::cout << \"Failed to start frame sink\" <<\n> std::endl;\n> +                       PR(\"Failed to start frame sink\\n\");\n>                         return ret;\n>                 }\n>         }\n>\n>         ret = camera_->start();\n>         if (ret) {\n> -               std::cout << \"Failed to start capture\" << std::endl;\n> +               PR(\"Failed to start capture\\n\");\n>                 if (sink_)\n>                         sink_->stop();\n>                 return ret;\n> @@ -297,7 +287,7 @@ int CameraSession::startCapture()\n>         for (std::unique_ptr<Request> &request : requests_) {\n>                 ret = queueRequest(request.get());\n>                 if (ret < 0) {\n> -                       std::cerr << \"Can't queue request\" << std::endl;\n> +                       EPR(\"Can't queue request\\n\");\n>                         camera_->stop();\n>                         if (sink_)\n>                                 sink_->stop();\n> @@ -306,13 +296,11 @@ int CameraSession::startCapture()\n>         }\n>\n>         if (captureLimit_)\n> -               std::cout << \"cam\" << cameraIndex_\n> -                         << \": Capture \" << captureLimit_ << \" frames\"\n> -                         << std::endl;\n> +               PR(\"cam{}: Capture {} frames\\n\", cameraIndex_,\n> +                  captureLimit_);\n>         else\n> -               std::cout << \"cam\" << cameraIndex_\n> -                         << \": Capture until user interrupts by SIGINT\"\n> -                         << std::endl;\n> +               PR(\"cam{}: Capture until user interrupts by SIGINT\\n\",\n> +                  cameraIndex_);\n>\n>         return 0;\n>  }\n> @@ -364,23 +352,23 @@ void CameraSession::processRequest(Request *request)\n>\n>         bool requeue = true;\n>\n> -       std::stringstream info;\n> -       info << ts / 1000000000 << \".\"\n> -            << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000\n> -            << \" (\" << std::fixed << std::setprecision(2) << fps << \"\n> fps)\";\n> +       auto sbuf = fmt::memory_buffer();\n> +       fmt::format_to(std::back_inserter(sbuf), \"{}.{:06} ({:.2f} fps)\",\n> +                      ts / 1000000000,\n> +                      ts / 1000 % 1000000,\n> +                      fps);\n>\n>         for (const auto &[stream, buffer] : buffers) {\n>                 const FrameMetadata &metadata = buffer->metadata();\n>\n> -               info << \" \" << streamNames_[stream]\n> -                    << \" seq: \" << std::setw(6) << std::setfill('0') <<\n> metadata.sequence\n> -                    << \" bytesused: \";\n> +               fmt::format_to(std::back_inserter(sbuf), \" {} seq: {:06}\n> bytesused: \",\n> +                              streamNames_[stream], metadata.sequence);\n>\n>                 unsigned int nplane = 0;\n>                 for (const FrameMetadata::Plane &plane :\n> metadata.planes()) {\n> -                       info << plane.bytesused;\n> +                       fmt::format_to(std::back_inserter(sbuf), \"{}\",\n> plane.bytesused);\n>                         if (++nplane < metadata.planes().size())\n> -                               info << \"/\";\n> +                               fmt::format_to(std::back_inserter(sbuf),\n> \"/\");\n>                 }\n>         }\n>\n> @@ -389,14 +377,13 @@ void CameraSession::processRequest(Request *request)\n>                         requeue = false;\n>         }\n>\n> -       std::cout << info.str() << std::endl;\n> +       PR(\"{}\\n\", fmt::to_string(sbuf));\n>\n>         if (printMetadata_) {\n>                 const ControlList &requestMetadata = request->metadata();\n>                 for (const auto &[key, value] : requestMetadata) {\n>                         const ControlId *id = controls::controls.at(key);\n> -                       std::cout << \"\\t\" << id->name() << \" = \"\n> -                                 << value.toString() << std::endl;\n> +                       PR(\"\\t{} = {}\\n\", id->name(), value.toString());\n>                 }\n>         }\n>\n> diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp\n> index 46e34eb5..84919ab3 100644\n> --- a/src/cam/drm.cpp\n> +++ b/src/cam/drm.cpp\n> @@ -10,12 +10,12 @@\n>  #include <algorithm>\n>  #include <errno.h>\n>  #include <fcntl.h>\n> -#include <iostream>\n>  #include <set>\n>  #include <string.h>\n>  #include <sys/ioctl.h>\n>  #include <sys/stat.h>\n>  #include <sys/types.h>\n> +#include <fmt/core.h>\n>\n>  #include <libcamera/framebuffer.h>\n>  #include <libcamera/geometry.h>\n> @@ -25,6 +25,9 @@\n>\n>  #include \"event_loop.h\"\n>\n> +#define PR(...) fmt::print(__VA_ARGS__)\n> +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> +\n>  namespace DRM {\n>\n>  Object::Object(Device *dev, uint32_t id, Type type)\n> @@ -178,9 +181,7 @@ Connector::Connector(Device *dev, const\n> drmModeConnector *connector)\n>  {\n>         auto typeName = connectorTypeNames.find(connector->connector_type);\n>         if (typeName == connectorTypeNames.end()) {\n> -               std::cerr\n> -                       << \"Invalid connector type \"\n> -                       << connector->connector_type << std::endl;\n> +               EPR(\"Invalid connector type {}}n\",\n> connector->connector_type);\n>                 typeName =\n> connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown);\n>         }\n>\n> @@ -213,9 +214,7 @@ Connector::Connector(Device *dev, const\n> drmModeConnector *connector)\n>                                                     return e.id() ==\n> encoderId;\n>                                             });\n>                 if (encoder == encoders.end()) {\n> -                       std::cerr\n> -                               << \"Encoder \" << encoderId << \" not found\"\n> -                               << std::endl;\n> +                       EPR(\"Encoder {} not found\\n\", encoderId);\n>                         continue;\n>                 }\n>\n> @@ -296,9 +295,7 @@ FrameBuffer::~FrameBuffer()\n>\n>                 if (ret == -1) {\n>                         ret = -errno;\n> -                       std::cerr\n> -                               << \"Failed to close GEM object: \"\n> -                               << strerror(-ret) << std::endl;\n> +                       EPR(\"Failed to close GEM object: {}\\n\",\n> strerror(-ret));\n>                 }\n>         }\n>\n> @@ -408,9 +405,8 @@ int Device::init()\n>         fd_ = open(name, O_RDWR | O_CLOEXEC);\n>         if (fd_ < 0) {\n>                 ret = -errno;\n> -               std::cerr\n> -                       << \"Failed to open DRM/KMS device \" << name << \": \"\n> -                       << strerror(-ret) << std::endl;\n> +               EPR(\"Failed to open DRM/KMS device {}: {}\\n\", name,\n> +                   strerror(-ret));\n>                 return ret;\n>         }\n>\n> @@ -421,9 +417,7 @@ int Device::init()\n>         ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1);\n>         if (ret < 0) {\n>                 ret = -errno;\n> -               std::cerr\n> -                       << \"Failed to enable atomic capability: \"\n> -                       << strerror(-ret) << std::endl;\n> +               EPR(\"Failed to enable atomic capability: {}\\n\",\n> strerror(-ret));\n>                 return ret;\n>         }\n>\n> @@ -448,9 +442,7 @@ int Device::getResources()\n>         };\n>         if (!resources) {\n>                 ret = -errno;\n> -               std::cerr\n> -                       << \"Failed to get DRM/KMS resources: \"\n> -                       << strerror(-ret) << std::endl;\n> +               EPR(\"Failed to get DRM/KMS resources: {}\\n\",\n> strerror(-ret));\n>                 return ret;\n>         }\n>\n> @@ -458,9 +450,7 @@ int Device::getResources()\n>                 drmModeCrtc *crtc = drmModeGetCrtc(fd_,\n> resources->crtcs[i]);\n>                 if (!crtc) {\n>                         ret = -errno;\n> -                       std::cerr\n> -                               << \"Failed to get CRTC: \" << strerror(-ret)\n> -                               << std::endl;\n> +                       EPR(\"Failed to get CRTC: {}\\n\", strerror(-ret));\n>                         return ret;\n>                 }\n>\n> @@ -476,9 +466,7 @@ int Device::getResources()\n>                         drmModeGetEncoder(fd_, resources->encoders[i]);\n>                 if (!encoder) {\n>                         ret = -errno;\n> -                       std::cerr\n> -                               << \"Failed to get encoder: \" <<\n> strerror(-ret)\n> -                               << std::endl;\n> +                       EPR(\"Failed to get encoder: {}\\n\", strerror(-ret));\n>                         return ret;\n>                 }\n>\n> @@ -494,9 +482,7 @@ int Device::getResources()\n>                         drmModeGetConnector(fd_, resources->connectors[i]);\n>                 if (!connector) {\n>                         ret = -errno;\n> -                       std::cerr\n> -                               << \"Failed to get connector: \" <<\n> strerror(-ret)\n> -                               << std::endl;\n> +                       EPR(\"Failed to get connector: {}\\n\",\n> strerror(-ret));\n>                         return ret;\n>                 }\n>\n> @@ -513,9 +499,7 @@ int Device::getResources()\n>         };\n>         if (!planes) {\n>                 ret = -errno;\n> -               std::cerr\n> -                       << \"Failed to get DRM/KMS planes: \"\n> -                       << strerror(-ret) << std::endl;\n> +               EPR(\"Failed to get DRM/KMS planes: {}\\n\", strerror(-ret));\n>                 return ret;\n>         }\n>\n> @@ -524,9 +508,7 @@ int Device::getResources()\n>                         drmModeGetPlane(fd_, planes->planes[i]);\n>                 if (!plane) {\n>                         ret = -errno;\n> -                       std::cerr\n> -                               << \"Failed to get plane: \" <<\n> strerror(-ret)\n> -                               << std::endl;\n> +                       EPR(\"Failed to get plane: {}\\n\", strerror(-ret));\n>                         return ret;\n>                 }\n>\n> @@ -556,9 +538,7 @@ int Device::getResources()\n>                 drmModePropertyRes *property = drmModeGetProperty(fd_, id);\n>                 if (!property) {\n>                         ret = -errno;\n> -                       std::cerr\n> -                               << \"Failed to get property: \" <<\n> strerror(-ret)\n> -                               << std::endl;\n> +                       EPR(\"Failed to get property: {}\\n\",\n> strerror(-ret));\n>                         continue;\n>                 }\n>\n> @@ -573,9 +553,8 @@ int Device::getResources()\n>         for (auto &object : objects_) {\n>                 ret = object.second->setup();\n>                 if (ret < 0) {\n> -                       std::cerr\n> -                               << \"Failed to setup object \" <<\n> object.second->id()\n> -                               << \": \" << strerror(-ret) << std::endl;\n> +                       EPR(\"Failed to setup object {}: {}\\n\",\n> +                           object.second->id(), strerror(-ret));\n>                         return ret;\n>                 }\n>         }\n> @@ -616,9 +595,8 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(\n>                         ret = drmPrimeFDToHandle(fd_, plane.fd.get(),\n> &handle);\n>                         if (ret < 0) {\n>                                 ret = -errno;\n> -                               std::cerr\n> -                                       << \"Unable to import framebuffer\n> dmabuf: \"\n> -                                       << strerror(-ret) << std::endl;\n> +                               EPR(\"Unable to import framebuffer dmabuf:\n> {}\\n\",\n> +                                   strerror(-ret));\n>                                 return nullptr;\n>                         }\n>\n> @@ -636,9 +614,7 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(\n>                             strides.data(), offsets, &fb->id_, 0);\n>         if (ret < 0) {\n>                 ret = -errno;\n> -               std::cerr\n> -                       << \"Failed to add framebuffer: \"\n> -                       << strerror(-ret) << std::endl;\n> +               EPR(\"Failed to add framebuffer: {}\\n\", strerror(-ret));\n>                 return nullptr;\n>         }\n>\n> diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp\n> index e25784c0..87aaf59a 100644\n> --- a/src/cam/event_loop.cpp\n> +++ b/src/cam/event_loop.cpp\n> @@ -10,7 +10,10 @@\n>  #include <assert.h>\n>  #include <event2/event.h>\n>  #include <event2/thread.h>\n> -#include <iostream>\n> +#include <fmt/core.h>\n> +\n> +#define PR(...) fmt::print(__VA_ARGS__)\n> +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n>\n>  EventLoop *EventLoop::instance_ = nullptr;\n>\n> @@ -71,13 +74,13 @@ void EventLoop::addEvent(int fd, EventType type,\n>         event->event_ = event_new(base_, fd, events,\n> &EventLoop::Event::dispatch,\n>                                   event.get());\n>         if (!event->event_) {\n> -               std::cerr << \"Failed to create event for fd \" << fd <<\n> std::endl;\n> +               EPR(\"Failed to create event for fd {}\\n\", fd);\n>                 return;\n>         }\n>\n>         int ret = event_add(event->event_, nullptr);\n>         if (ret < 0) {\n> -               std::cerr << \"Failed to add event for fd \" << fd <<\n> std::endl;\n> +               EPR(\"Failed to add event for fd {}\\n\", fd);\n>                 return;\n>         }\n>\n> diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp\n> index 45213d4a..86e2118c 100644\n> --- a/src/cam/file_sink.cpp\n> +++ b/src/cam/file_sink.cpp\n> @@ -7,11 +7,12 @@\n>\n>  #include <assert.h>\n>  #include <fcntl.h>\n> -#include <iomanip>\n> -#include <iostream>\n> -#include <sstream>\n>  #include <string.h>\n>  #include <unistd.h>\n> +#include <fmt/core.h>\n> +\n> +#define PR(...) fmt::print(__VA_ARGS__)\n> +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n>\n>  #include <libcamera/camera.h>\n>\n> @@ -70,10 +71,10 @@ void FileSink::writeBuffer(const Stream *stream,\n> FrameBuffer *buffer)\n>\n>         pos = filename.find_first_of('#');\n>         if (pos != std::string::npos) {\n> -               std::stringstream ss;\n> -               ss << streamNames_[stream] << \"-\" << std::setw(6)\n> -                  << std::setfill('0') << buffer->metadata().sequence;\n> -               filename.replace(pos, 1, ss.str());\n> +               auto s = fmt::format(\"{}-{:06}\",\n> +                                    streamNames_[stream],\n> +                                    buffer->metadata().sequence);\n> +               filename.replace(pos, 1, s);\n>         }\n>\n>         fd = open(filename.c_str(), O_CREAT | O_WRONLY |\n> @@ -81,8 +82,7 @@ void FileSink::writeBuffer(const Stream *stream,\n> FrameBuffer *buffer)\n>                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |\n> S_IWOTH);\n>         if (fd == -1) {\n>                 ret = -errno;\n> -               std::cerr << \"failed to open file \" << filename << \": \"\n> -                         << strerror(-ret) << std::endl;\n> +               EPR(\"failed to open file {}: {}\\n\", filename,\n> strerror(-ret));\n>                 return;\n>         }\n>\n> @@ -95,20 +95,17 @@ void FileSink::writeBuffer(const Stream *stream,\n> FrameBuffer *buffer)\n>                 unsigned int length = std::min<unsigned\n> int>(meta.bytesused, data.size());\n>\n>                 if (meta.bytesused > data.size())\n> -                       std::cerr << \"payload size \" << meta.bytesused\n> -                                 << \" larger than plane size \" <<\n> data.size()\n> -                                 << std::endl;\n> +                       EPR(\"payload size {} larger than plane size {}\\n\",\n> +                           meta.bytesused, data.size());\n>\n>                 ret = ::write(fd, data.data(), length);\n>                 if (ret < 0) {\n>                         ret = -errno;\n> -                       std::cerr << \"write error: \" << strerror(-ret)\n> -                                 << std::endl;\n> +                       EPR(\"write error: {}\\n\", strerror(-ret));\n>                         break;\n>                 } else if (ret != (int)length) {\n> -                       std::cerr << \"write error: only \" << ret\n> -                                 << \" bytes written instead of \"\n> -                                 << length << std::endl;\n> +                       EPR(\"write error: only {} bytes written instead of\n> {}\\n\",\n> +                           ret, length);\n>                         break;\n>                 }\n>         }\n> diff --git a/src/cam/image.cpp b/src/cam/image.cpp\n> index fe2cc6da..73bcf915 100644\n> --- a/src/cam/image.cpp\n> +++ b/src/cam/image.cpp\n> @@ -9,11 +9,14 @@\n>\n>  #include <assert.h>\n>  #include <errno.h>\n> -#include <iostream>\n>  #include <map>\n>  #include <string.h>\n>  #include <sys/mman.h>\n>  #include <unistd.h>\n> +#include <fmt/core.h>\n> +\n> +#define PR(...) fmt::print(__VA_ARGS__)\n> +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n>\n>  using namespace libcamera;\n>\n> @@ -49,10 +52,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const\n> FrameBuffer *buffer, MapMode\n>\n>                 if (plane.offset > length ||\n>                     plane.offset + plane.length > length) {\n> -                       std::cerr << \"plane is out of buffer: buffer\n> length=\"\n> -                                 << length << \", plane offset=\" <<\n> plane.offset\n> -                                 << \", plane length=\" << plane.length\n> -                                 << std::endl;\n> +                       EPR(\"plane is out of buffer: buffer length={},\n> plane offset={}, plane length={}\\n\",\n> +                           length, plane.offset, plane.length);\n>                         return nullptr;\n>                 }\n>                 size_t &mapLength = mappedBuffers[fd].mapLength;\n> @@ -68,8 +69,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const\n> FrameBuffer *buffer, MapMode\n>                                              MAP_SHARED, fd, 0);\n>                         if (address == MAP_FAILED) {\n>                                 int error = -errno;\n> -                               std::cerr << \"Failed to mmap plane: \"\n> -                                         << strerror(-error) << std::endl;\n> +                               EPR(\"Failed to mmap plane: {}\\n\",\n> +                                   strerror(-error));\n>                                 return nullptr;\n>                         }\n>\n> diff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp\n> index 7add81a6..823b75e4 100644\n> --- a/src/cam/kms_sink.cpp\n> +++ b/src/cam/kms_sink.cpp\n> @@ -10,10 +10,13 @@\n>  #include <array>\n>  #include <algorithm>\n>  #include <assert.h>\n> -#include <iostream>\n>  #include <memory>\n>  #include <stdint.h>\n>  #include <string.h>\n> +#include <fmt/core.h>\n> +\n> +#define PR(...) fmt::print(__VA_ARGS__)\n> +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n>\n>  #include <libcamera/camera.h>\n>  #include <libcamera/formats.h>\n> @@ -54,11 +57,9 @@ KMSSink::KMSSink(const std::string &connectorName)\n>\n>         if (!connector_) {\n>                 if (!connectorName.empty())\n> -                       std::cerr\n> -                               << \"Connector \" << connectorName << \" not\n> found\"\n> -                               << std::endl;\n> +                       EPR(\"Connector {} not found\\n\", connectorName);\n>                 else\n> -                       std::cerr << \"No connected connector found\" <<\n> std::endl;\n> +                       EPR(\"No connected connector found\\n\");\n>                 return;\n>         }\n>\n> @@ -119,7 +120,7 @@ int KMSSink::configure(const\n> libcamera::CameraConfiguration &config)\n>                                                       mode.vdisplay ==\n> cfg.size.height;\n>                                        });\n>         if (iter == modes.end()) {\n> -               std::cerr << \"No mode matching \" << cfg.size << std::endl;\n> +               EPR(\"No mode matching {}\\n\", cfg.size);\n>                 return -EINVAL;\n>         }\n>\n> @@ -192,17 +193,12 @@ int KMSSink::configurePipeline(const\n> libcamera::PixelFormat &format)\n>  {\n>         const int ret = selectPipeline(format);\n>         if (ret) {\n> -               std::cerr\n> -                       << \"Unable to find display pipeline for format \"\n> -                       << format << std::endl;\n> -\n> +               EPR(\"Unable to find display pipeline for format {}\\n\",\n> format);\n>                 return ret;\n>         }\n>\n> -       std::cout\n> -               << \"Using KMS plane \" << plane_->id() << \", CRTC \" <<\n> crtc_->id()\n> -               << \", connector \" << connector_->name()\n> -               << \" (\" << connector_->id() << \")\" << std::endl;\n> +       PR(\"Using KMS plane {}, CRTC {}, connector {} ({})\\n\",\n> +          plane_->id(), crtc_->id(), connector_->name(),\n> connector_->id());\n>\n>         return 0;\n>  }\n> @@ -228,9 +224,8 @@ int KMSSink::start()\n>\n>         ret = request->commit(DRM::AtomicRequest::FlagAllowModeset);\n>         if (ret < 0) {\n> -               std::cerr\n> -                       << \"Failed to disable CRTCs and planes: \"\n> -                       << strerror(-ret) << std::endl;\n> +               EPR(\"Failed to disable CRTCs and planes: {}\\n\",\n> +                   strerror(-ret));\n>                 return ret;\n>         }\n>\n> @@ -250,9 +245,7 @@ int KMSSink::stop()\n>\n>         int ret = request.commit(DRM::AtomicRequest::FlagAllowModeset);\n>         if (ret < 0) {\n> -               std::cerr\n> -                       << \"Failed to stop display pipeline: \"\n> -                       << strerror(-ret) << std::endl;\n> +               EPR(\"Failed to stop display pipeline: {}\\n\",\n> strerror(-ret));\n>                 return ret;\n>         }\n>\n> @@ -312,9 +305,8 @@ bool KMSSink::processRequest(libcamera::Request\n> *camRequest)\n>         if (!queued_) {\n>                 int ret = drmRequest->commit(flags);\n>                 if (ret < 0) {\n> -                       std::cerr\n> -                               << \"Failed to commit atomic request: \"\n> -                               << strerror(-ret) << std::endl;\n> +                       EPR(\"Failed to commit atomic request: {}\\n\",\n> +                           strerror(-ret));\n>                         /* \\todo Implement error handling */\n>                 }\n>\n> diff --git a/src/cam/main.cpp b/src/cam/main.cpp\n> index c7f664b9..03615dc9 100644\n> --- a/src/cam/main.cpp\n> +++ b/src/cam/main.cpp\n> @@ -6,10 +6,9 @@\n>   */\n>\n>  #include <atomic>\n> -#include <iomanip>\n> -#include <iostream>\n>  #include <signal.h>\n>  #include <string.h>\n> +#include <fmt/core.h>\n>\n>  #include <libcamera/libcamera.h>\n>  #include <libcamera/property_ids.h>\n> @@ -78,8 +77,7 @@ int CamApp::init(int argc, char **argv)\n>\n>         ret = cm_->start();\n>         if (ret) {\n> -               std::cout << \"Failed to start camera manager: \"\n> -                         << strerror(-ret) << std::endl;\n> +               fmt::print(\"Failed to start camera manager: {}\\n\", -ret);\n>                 return ret;\n>         }\n>\n> @@ -173,12 +171,12 @@ int CamApp::parseOptions(int argc, char *argv[])\n>\n>  void CamApp::cameraAdded(std::shared_ptr<Camera> cam)\n>  {\n> -       std::cout << \"Camera Added: \" << cam->id() << std::endl;\n> +       fmt::print(\"Camera Added: {}\\n\", cam->id());\n>  }\n>\n>  void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n>  {\n> -       std::cout << \"Camera Removed: \" << cam->id() << std::endl;\n> +       fmt::print(\"Camera Removed: {}\\n\", cam->id());\n>  }\n>\n>  void CamApp::captureDone()\n> @@ -193,11 +191,11 @@ int CamApp::run()\n>\n>         /* 1. List all cameras. */\n>         if (options_.isSet(OptList)) {\n> -               std::cout << \"Available cameras:\" << std::endl;\n> +               fmt::print(\"Available cameras:\\n\");\n>\n>                 unsigned int index = 1;\n>                 for (const std::shared_ptr<Camera> &cam : cm_->cameras()) {\n> -                       std::cout << index << \": \" <<\n> cameraName(cam.get()) << std::endl;\n> +                       fmt::print(\"{}: {}\\n\", cameraName(cam.get()),\n> index);\n>                         index++;\n>                 }\n>         }\n> @@ -215,12 +213,12 @@ int CamApp::run()\n>                                                                 index,\n>\n> camera.children());\n>                         if (!session->isValid()) {\n> -                               std::cout << \"Failed to create camera\n> session\" << std::endl;\n> +                               fmt::print(\"Failed to create camera\n> session\\n\");\n>                                 return -EINVAL;\n>                         }\n>\n> -                       std::cout << \"Using camera \" <<\n> session->camera()->id()\n> -                                 << \" as cam\" << index << std::endl;\n> +                       fmt::print(\"Using camera{} as cam{}\\n\",\n> +                                  session->camera()->id(), index);\n>\n>                         session->captureDone.connect(this,\n> &CamApp::captureDone);\n>\n> @@ -250,7 +248,7 @@ int CamApp::run()\n>\n>                 ret = session->start();\n>                 if (ret) {\n> -                       std::cout << \"Failed to start camera session\" <<\n> std::endl;\n> +                       fmt::print(\"Failed to start camera session\\n\");\n>                         return ret;\n>                 }\n>\n> @@ -259,8 +257,8 @@ int CamApp::run()\n>\n>         /* 5. Enable hotplug monitoring. */\n>         if (options_.isSet(OptMonitor)) {\n> -               std::cout << \"Monitoring new hotplug and unplug events\" <<\n> std::endl;\n> -               std::cout << \"Press Ctrl-C to interrupt\" << std::endl;\n> +               fmt::print(\"Monitoring new hotplug and unplug events\\n\");\n> +               fmt::print(\"Press Ctrl-C to interrupt\\n\");\n>\n>                 cm_->cameraAdded.connect(this, &CamApp::cameraAdded);\n>                 cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);\n> @@ -323,7 +321,7 @@ std::string CamApp::cameraName(const Camera *camera)\n>\n>  void signalHandler([[maybe_unused]] int signal)\n>  {\n> -       std::cout << \"Exiting\" << std::endl;\n> +       fmt::print(\"Exiting\");\n>         CamApp::instance()->quit();\n>  }\n>\n> diff --git a/src/cam/meson.build b/src/cam/meson.build\n> index 5bab8c9e..2b47383d 100644\n> --- a/src/cam/meson.build\n> +++ b/src/cam/meson.build\n> @@ -7,6 +7,8 @@ if not libevent.found()\n>      subdir_done()\n>  endif\n>\n> +libfmt_dep = dependency('fmt')\n> +\n>  cam_enabled = true\n>\n>  cam_sources = files([\n> @@ -25,7 +27,7 @@ cam_cpp_args = []\n>  libdrm = dependency('libdrm', required : false)\n>\n>  if libdrm.found()\n> -    cam_cpp_args += [ '-DHAVE_KMS' ]\n> +    cam_cpp_args += [ '-DHAVE_KMS', ]\n>      cam_sources += files([\n>          'drm.cpp',\n>          'kms_sink.cpp'\n> @@ -38,6 +40,7 @@ cam  = executable('cam', cam_sources,\n>                        libcamera_public,\n>                        libdrm,\n>                        libevent,\n> +                      libfmt_dep,\n>                    ],\n>                    cpp_args : cam_cpp_args,\n>                    install : true)\n> diff --git a/src/cam/options.cpp b/src/cam/options.cpp\n> index 4f7e8691..c9979385 100644\n> --- a/src/cam/options.cpp\n> +++ b/src/cam/options.cpp\n> @@ -7,9 +7,11 @@\n>\n>  #include <assert.h>\n>  #include <getopt.h>\n> -#include <iomanip>\n> -#include <iostream>\n>  #include <string.h>\n> +#include <fmt/core.h>\n> +\n> +#define PR(...) fmt::print(__VA_ARGS__)\n> +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n>\n>  #include \"options.h\"\n>\n> @@ -390,26 +392,23 @@ KeyValueParser::Options KeyValueParser::parse(const\n> char *arguments)\n>                         continue;\n>\n>                 if (optionsMap_.find(key) == optionsMap_.end()) {\n> -                       std::cerr << \"Invalid option \" << key << std::endl;\n> +                       EPR(\"Invalid option {}\\n\", key);\n>                         return options;\n>                 }\n>\n>                 OptionArgument arg = optionsMap_[key].argument;\n>                 if (value.empty() && arg == ArgumentRequired) {\n> -                       std::cerr << \"Option \" << key << \" requires an\n> argument\"\n> -                                 << std::endl;\n> +                       EPR(\"Option {} requires an argument\\n\", key);\n>                         return options;\n>                 } else if (!value.empty() && arg == ArgumentNone) {\n> -                       std::cerr << \"Option \" << key << \" takes no\n> argument\"\n> -                                 << std::endl;\n> +                       EPR(\"Option {} takes no argument\\n\", key);\n>                         return options;\n>                 }\n>\n>                 const Option &option = optionsMap_[key];\n>                 if (!options.parseValue(key, option, value.c_str())) {\n> -                       std::cerr << \"Failed to parse '\" << value << \"' as\n> \"\n> -                                 << option.typeName() << \" for option \"\n> << key\n> -                                 << std::endl;\n> +                       EPR(\"Failed to parse '{}' as {} for option {}\\n\",\n> +                           value, option.typeName(), key);\n>                         return options;\n>                 }\n>         }\n> @@ -453,16 +452,16 @@ void KeyValueParser::usage(int indent)\n>                                 argument += \"]\";\n>                 }\n>\n> -               std::cerr << std::setw(indent) << argument;\n> +               EPR(\"{:{}}\", argument, indent);\n>\n>                 for (const char *help = option.help, *end = help; end;) {\n>                         end = strchr(help, '\\n');\n>                         if (end) {\n> -                               std::cerr << std::string(help, end - help\n> + 1);\n> -                               std::cerr << std::setw(indent) << \" \";\n> +                               EPR(std::string(help, end - help + 1));\n> +                               EPR(\"{:{}}\", \"\", indent);\n>                                 help = end + 1;\n>                         } else {\n> -                               std::cerr << help << std::endl;\n> +                               EPR(\"{}\\n\", help);\n>                         }\n>                 }\n>         }\n> @@ -929,10 +928,10 @@ OptionsParser::Options OptionsParser::parse(int\n> argc, char **argv)\n>\n>                 if (c == '?' || c == ':') {\n>                         if (c == '?')\n> -                               std::cerr << \"Invalid option \";\n> +                               EPR(\"Invalid option \");\n>                         else\n> -                               std::cerr << \"Missing argument for option\n> \";\n> -                       std::cerr << argv[optind - 1] << std::endl;\n> +                               EPR(\"Missing argument for option \");\n> +                       EPR(\"{}\\n\", argv[optind - 1]);\n>\n>                         usage();\n>                         return options;\n> @@ -946,8 +945,7 @@ OptionsParser::Options OptionsParser::parse(int argc,\n> char **argv)\n>         }\n>\n>         if (optind < argc) {\n> -               std::cerr << \"Invalid non-option argument '\" <<\n> argv[optind]\n> -                         << \"'\" << std::endl;\n> +               EPR(\"Invalid non-option argument '{}'\\n\", argv[optind]);\n>                 usage();\n>                 return options;\n>         }\n> @@ -992,14 +990,9 @@ void OptionsParser::usage()\n>\n>         indent = (indent + 7) / 8 * 8;\n>\n> -       std::cerr << \"Options:\" << std::endl;\n> -\n> -       std::ios_base::fmtflags f(std::cerr.flags());\n> -       std::cerr << std::left;\n> +       EPR(\"Options:\\n\");\n>\n>         usageOptions(options_, indent);\n> -\n> -       std::cerr.flags(f);\n>  }\n>\n>  void OptionsParser::usageOptions(const std::list<Option> &options,\n> @@ -1036,16 +1029,16 @@ void OptionsParser::usageOptions(const\n> std::list<Option> &options,\n>                 if (option.isArray)\n>                         argument += \" ...\";\n>\n> -               std::cerr << std::setw(indent) << argument;\n> +               EPR(\"{:{}}\", argument, indent);\n>\n> -               for (const char *help = option.help, *end = help; end; ) {\n> +               for (const char *help = option.help, *end = help; end;) {\n>                         end = strchr(help, '\\n');\n>                         if (end) {\n> -                               std::cerr << std::string(help, end - help\n> + 1);\n> -                               std::cerr << std::setw(indent) << \" \";\n> +                               EPR(std::string(help, end - help + 1));\n> +                               EPR(\"{:{}}\", \"\", indent);\n>                                 help = end + 1;\n>                         } else {\n> -                               std::cerr << help << std::endl;\n> +                               EPR(\"{}\\n\", help);\n>                         }\n>                 }\n>\n> @@ -1060,8 +1053,8 @@ void OptionsParser::usageOptions(const\n> std::list<Option> &options,\n>                 return;\n>\n>         for (const Option *option : parentOptions) {\n> -               std::cerr << std::endl << \"Options valid in the context of\n> \"\n> -                         << option->optionName() << \":\" << std::endl;\n> +               EPR(\"\\nOptions valid in the context of {}:\\n\",\n> +                   option->optionName());\n>                 usageOptions(option->children, indent);\n>         }\n>  }\n> @@ -1125,15 +1118,14 @@ bool OptionsParser::parseValue(const Option\n> &option, const char *arg,\n>\n>         std::tie(options, error) = childOption(option.parent, options);\n>         if (error) {\n> -               std::cerr << \"Option \" << option.optionName() << \"\n> requires a \"\n> -                         << error->optionName() << \" context\" <<\n> std::endl;\n> +               EPR(\"Option {} requires a {} context\\n\",\n> +                   option.optionName(), error->optionName());\n>                 return false;\n>         }\n>\n>         if (!options->parseValue(option.opt, option, arg)) {\n> -               std::cerr << \"Can't parse \" << option.typeName()\n> -                         << \" argument for option \" << option.optionName()\n> -                         << std::endl;\n> +               EPR(\"Can't parse {} argument for option {}\\n\",\n> +                   option.typeName(), option.optionName());\n>                 return false;\n>         }\n>\n> diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp\n> index 150bd27c..666862eb 100644\n> --- a/src/cam/stream_options.cpp\n> +++ b/src/cam/stream_options.cpp\n> @@ -6,7 +6,10 @@\n>   */\n>  #include \"stream_options.h\"\n>\n> -#include <iostream>\n> +#include <fmt/core.h>\n> +\n> +#define PR(...) fmt::print(__VA_ARGS__)\n> +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n>\n>  using namespace libcamera;\n>\n> @@ -30,8 +33,7 @@ KeyValueParser::Options\n> StreamKeyValueParser::parse(const char *arguments)\n>\n>         if (options.valid() && options.isSet(\"role\") &&\n>             !parseRole(&role, options)) {\n> -               std::cerr << \"Unknown stream role \"\n> -                         << options[\"role\"].toString() << std::endl;\n> +               EPR(\"Unknown stream role {}\\n\",\n> options[\"role\"].toString());\n>                 options.invalidate();\n>         }\n>\n> @@ -64,7 +66,7 @@ int\n> StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,\n>                                               const OptionValue &values)\n>  {\n>         if (!config) {\n> -               std::cerr << \"No configuration provided\" << std::endl;\n> +               EPR(\"No configuration provided\\n\");\n>                 return -EINVAL;\n>         }\n>\n> @@ -75,12 +77,8 @@ int\n> StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,\n>         const std::vector<OptionValue> &streamParameters =\n> values.toArray();\n>\n>         if (config->size() != streamParameters.size()) {\n> -               std::cerr\n> -                       << \"Number of streams in configuration \"\n> -                       << config->size()\n> -                       << \" does not match number of streams parsed \"\n> -                       << streamParameters.size()\n> -                       << std::endl;\n> +               EPR(\"Number of streams in configuration {} does not match\n> number of streams parsed {}\\n\",\n> +                   config->size(), streamParameters.size());\n>                 return -EINVAL;\n>         }\n>\n> --\n> 2.34.1\n>\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 2296DC3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 09:49:34 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5191165648;\n\tTue, 10 May 2022 11:49:33 +0200 (CEST)","from mail-lj1-x22a.google.com (mail-lj1-x22a.google.com\n\t[IPv6:2a00:1450:4864:20::22a])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5B6F56563E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 11:49:31 +0200 (CEST)","by mail-lj1-x22a.google.com with SMTP id 16so20161254lju.13\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 02:49:31 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652176173;\n\tbh=GBAiT10EZP6SHqK78B23Eu2f5NXckTV1p7VrwDGEc6Q=;\n\th=References:In-Reply-To:Date:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=zdmVj43L8QBh9b9w8wBo1DI0P6zFlphPJzRcTHFhKazuY0T2ZGFUtrRAlc6RsLgFF\n\tFtPLcdHM/Z4exEGpIunx1HTY50ZWzoGgHH8PIf90/7HcCGJ2Bbm6R+DPEI5hggdDal\n\tH0jWz3CYJLcnYQTP7o7ejySlGL+gykkBZ+yEcUiwbwmkcKtvnwCeTF69MzJblGls5k\n\t9ZiYBwbgRiWp9qwF37k+5GB114YVlHYH7qNFkPSc2ERM9E/ReMxecr2ULgATc/L4ln\n\t50LrrIIKNFz4zuRgx0S1oe7a4dUfyEIJIfU0Z1iqxxBf44ivY/dPPFhV7lIDlqCaE8\n\tvTFPuT2vv6epg==","v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=skeL0B4vvtJf/ClBrNfn7648NWHiWrB+ry57goqEY8c=;\n\tb=tG5FHKf254pEaPYESOMIJ2I2OGGkYtpLYSLEBQoBxcDkNKK/OkqnxV2MheOgx7JCBU\n\t11lFzBsPYwxd51wnw5CVcJYWQc/RU+1UOK/e7vcF9E9vlGxmkt76au909uB7uufiKHtH\n\tFo+qef/R9gSEwJCEF0Gfj7lpH9t8DaEPKLLFLLwEogJFTPE/Mqv+/x3BSDGypM5TgZlI\n\tKM40kDcEkySRsZjpAJzQwOOKg18PKAeBTfiMYI4dcpDPxpD50DvLFXbFMxE321FgiGgZ\n\tXvSX7tTQSM2xE5g0nuSjbuXZorptOouYSEt8/KcGUo8Pt9Ao0zCQ52OhOH21qOg9KAuF\n\tWhgg=="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=raspberrypi.com\n\theader.i=@raspberrypi.com\n\theader.b=\"tG5FHKf2\"; dkim-atps=neutral","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=skeL0B4vvtJf/ClBrNfn7648NWHiWrB+ry57goqEY8c=;\n\tb=xsiAgxum+okfIvkYs2cXvueW1RUGaEW62hrL8IOZ39DqoPFahdzsl1Pt0gkzyE3bxG\n\tUzWmnLv44SgWOD50UV1/bXJOS6BwYC2jj6wDQt4WGuybP8mE2WLf15yqD2+/hgqWyzk4\n\tLvgi+5IMN7icdCXSWD2Tk5pJTS5iCF129hmdyAiK+d+00DIF/v66qNm5IuV0Ei6rC/xC\n\tuzKXIR0lf6C+5KfxdzdsAdDhF7I/nxCjVgwCV7hV2vnDbjggE4N56x9TjE7cwQif9mh+\n\tSLH9Mc1mmCeiRn9dOYyN2N8Ijf2OOGoZkWvXQ0B9gTgyWo/BVuwhlZ+19tMmakvcCZ9x\n\t6xlQ==","X-Gm-Message-State":"AOAM5335uYeM+XW8Xb6YyMtc7j+jwjHUaG0D4NNLk5FMYzkpeCPQXGtW\n\tpd7C8VZqRfXu5ZkQwm/ERQHsqqbqMMXT3OMIiYilP+RnbPo=","X-Google-Smtp-Source":"ABdhPJyAnA9b43U6UPYBKVCsqa5rtnqkdyolvMHpqusHKTN9yAvVaJ2cNQPTnP6hcWpAb4kaPi6B9dE1ShZjC9scOdc=","X-Received":"by 2002:a2e:3207:0:b0:24f:11aa:4017 with SMTP id\n\ty7-20020a2e3207000000b0024f11aa4017mr13615278ljy.380.1652176170422;\n\tTue, 10 May 2022 02:49:30 -0700 (PDT)","MIME-Version":"1.0","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>","In-Reply-To":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>","Date":"Tue, 10 May 2022 10:49:14 +0100","Message-ID":"<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"000000000000bea6b805dea53d09\"","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Naushir Patuck via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Naushir Patuck <naush@raspberrypi.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22930,"web_url":"https://patchwork.libcamera.org/comment/22930/","msgid":"<165217739174.2416244.12652784382118148891@Monstersaurus>","date":"2022-05-10T10:09:51","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Naushir Patuck (2022-05-10 10:49:14)\n> Hi Tomi,\n> \n> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel <\n> libcamera-devel@lists.libcamera.org> wrote:\n> \n> > This is just a conversation starter, not for merging. I really like\n> > libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > is the first library I add to any new C++ project I make.\n> >\n> > You can find more information about libfmt from:\n> >\n> > https://github.com/fmtlib/fmt\n> > https://fmt.dev/latest/index.html\n> >\n> > This patch is just a crude conversion with ugly macros to showcase what\n> > the formatting code might look like.\n> >\n> > libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > than iostreams, but for the size it didn't seem to be true in cam's case\n> > as the tests below show. However, simple prints did reduce the exe size,\n> > but the few more complex ones increased the size.\n> >\n> > Size tests with gcc 11.2.0-19ubuntu1\n> >\n> > - Without libfmt\n> >\n> > debug           3523400\n> > debug lto       3269368\n> > release         223056\n> > release lto     172280\n> >\n> > - With libfmt\n> >\n> > debug           4424256\n> > debug lto       4143840\n> > release         303952\n> > release lto     252640\n> >\n> > Above shows that cam's size clearly increases with libfmt. However, the\n> > increase really comes only from one case, the use of fmt::memory_buffer\n> > and std::back_inserter. Converting that code to use fmt::format() and\n> > naive string append gives:\n> >\n> > release with string append      233680\n> > release lto with string append  186936\n> >\n> > Also, if I add another use of fmt::memory_buffer and std::back_inserter\n> > to another file, I see much less increase in the size:\n> >\n> > release lto with two uses of memory_buffer, back_inserter       256736\n> >\n> > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> >\n> \n> For what it's worth, I absolutely loathe formatting in std iostream, and\n> libfmt is a wonderful alternative.\n> It also is close enough to the C++20 std::format implementation that\n> eventual porting would be low effort.  So I am all for this change :)\n\nI've never used (yet) {fmt}, but I've only heard good things about\nperformance, and of course it's headed into the standard, so I also\nthink there is some good merit to be found in this development.\n\n--\nKieran\n\n\n> \n> Regards,\n> Naush\n> \n> \n> \n> > ---\n> >  src/cam/camera_session.cpp | 105 ++++++++++++++++---------------------\n> >  src/cam/drm.cpp            |  68 ++++++++----------------\n> >  src/cam/event_loop.cpp     |   9 ++--\n> >  src/cam/file_sink.cpp      |  31 +++++------\n> >  src/cam/image.cpp          |  15 +++---\n> >  src/cam/kms_sink.cpp       |  38 ++++++--------\n> >  src/cam/main.cpp           |  28 +++++-----\n> >  src/cam/meson.build        |   5 +-\n> >  src/cam/options.cpp        |  66 ++++++++++-------------\n> >  src/cam/stream_options.cpp |  18 +++----\n> >  10 files changed, 165 insertions(+), 218 deletions(-)\n> >\n> > diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp\n> > index efffafbf..7843c3fd 100644\n> > --- a/src/cam/camera_session.cpp\n> > +++ b/src/cam/camera_session.cpp\n> > @@ -5,10 +5,9 @@\n> >   * camera_session.cpp - Camera capture session\n> >   */\n> >\n> > -#include <iomanip>\n> > -#include <iostream>\n> >  #include <limits.h>\n> > -#include <sstream>\n> > +#include <fmt/format.h>\n> > +#include <fmt/ostream.h>\n> >\n> >  #include <libcamera/control_ids.h>\n> >  #include <libcamera/property_ids.h>\n> > @@ -22,6 +21,9 @@\n> >  #include \"main.h\"\n> >  #include \"stream_options.h\"\n> >\n> > +#define PR(...) fmt::print(__VA_ARGS__)\n> > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > +\n> >  using namespace libcamera;\n> >\n> >  CameraSession::CameraSession(CameraManager *cm,\n> > @@ -40,13 +42,12 @@ CameraSession::CameraSession(CameraManager *cm,\n> >                 camera_ = cm->get(cameraId);\n> >\n> >         if (!camera_) {\n> > -               std::cerr << \"Camera \" << cameraId << \" not found\" <<\n> > std::endl;\n> > +               EPR(\"Camera {} not found\\n\", cameraId);\n> >                 return;\n> >         }\n> >\n> >         if (camera_->acquire()) {\n> > -               std::cerr << \"Failed to acquire camera \" << cameraId\n> > -                         << std::endl;\n> > +               EPR(\"Failed to acquire camera {}\", cameraId);\n> >                 return;\n> >         }\n> >\n> > @@ -55,15 +56,14 @@ CameraSession::CameraSession(CameraManager *cm,\n> >         std::unique_ptr<CameraConfiguration> config =\n> >                 camera_->generateConfiguration(roles);\n> >         if (!config || config->size() != roles.size()) {\n> > -               std::cerr << \"Failed to get default stream configuration\"\n> > -                         << std::endl;\n> > +               EPR(\"Failed to get default stream configuration\\n\");\n> >                 return;\n> >         }\n> >\n> >         /* Apply configuration if explicitly requested. */\n> >         if (StreamKeyValueParser::updateConfiguration(config.get(),\n> >\n> > options_[OptStream])) {\n> > -               std::cerr << \"Failed to update configuration\" << std::endl;\n> > +               EPR(\"Failed to update configuration\\n\");\n> >                 return;\n> >         }\n> >\n> > @@ -72,20 +72,17 @@ CameraSession::CameraSession(CameraManager *cm,\n> >  #ifdef HAVE_KMS\n> >         if (options_.isSet(OptDisplay)) {\n> >                 if (options_.isSet(OptFile)) {\n> > -                       std::cerr << \"--display and --file options are\n> > mutually exclusive\"\n> > -                                 << std::endl;\n> > +                       EPR(\"--display and --file options are mutually\n> > exclusive\\n\");\n> >                         return;\n> >                 }\n> >\n> >                 if (roles.size() != 1) {\n> > -                       std::cerr << \"Display doesn't support multiple\n> > streams\"\n> > -                                 << std::endl;\n> > +                       EPR(\"Display doesn't support multiple streams\\n\");\n> >                         return;\n> >                 }\n> >\n> >                 if (roles[0] != StreamRole::Viewfinder) {\n> > -                       std::cerr << \"Display requires a viewfinder stream\"\n> > -                                 << std::endl;\n> > +                       EPR(\"Display requires a viewfinder stream\\n\");\n> >                         return;\n> >                 }\n> >         }\n> > @@ -97,15 +94,14 @@ CameraSession::CameraSession(CameraManager *cm,\n> >\n> >         case CameraConfiguration::Adjusted:\n> >                 if (strictFormats) {\n> > -                       std::cout << \"Adjusting camera configuration\n> > disallowed by --strict-formats argument\"\n> > -                                 << std::endl;\n> > +                       PR(\"Adjusting camera configuration disallowed by\n> > --strict-formats argument\\n\");\n> >                         return;\n> >                 }\n> > -               std::cout << \"Camera configuration adjusted\" << std::endl;\n> > +               PR(\"Camera configuration adjusted\\n\");\n> >                 break;\n> >\n> >         case CameraConfiguration::Invalid:\n> > -               std::cout << \"Camera configuration invalid\" << std::endl;\n> > +               PR(\"Camera configuration invalid\\n\");\n> >                 return;\n> >         }\n> >\n> > @@ -121,8 +117,7 @@ CameraSession::~CameraSession()\n> >  void CameraSession::listControls() const\n> >  {\n> >         for (const auto &[id, info] : camera_->controls()) {\n> > -               std::cout << \"Control: \" << id->name() << \": \"\n> > -                         << info.toString() << std::endl;\n> > +               PR(\"Control: {}: {}}n\", id->name(), info.toString());\n> >         }\n> >  }\n> >\n> > @@ -131,8 +126,7 @@ void CameraSession::listProperties() const\n> >         for (const auto &[key, value] : camera_->properties()) {\n> >                 const ControlId *id = properties::properties.at(key);\n> >\n> > -               std::cout << \"Property: \" << id->name() << \" = \"\n> > -                         << value.toString() << std::endl;\n> > +               PR(\"Property: {} = {}\\n\", id->name(), value.toString());\n> >         }\n> >  }\n> >\n> > @@ -140,17 +134,15 @@ void CameraSession::infoConfiguration() const\n> >  {\n> >         unsigned int index = 0;\n> >         for (const StreamConfiguration &cfg : *config_) {\n> > -               std::cout << index << \": \" << cfg.toString() << std::endl;\n> > +               PR(\"{}: {}\\n\", index, cfg.toString());\n> >\n> >                 const StreamFormats &formats = cfg.formats();\n> >                 for (PixelFormat pixelformat : formats.pixelformats()) {\n> > -                       std::cout << \" * Pixelformat: \"\n> > -                                 << pixelformat << \" \"\n> > -                                 << formats.range(pixelformat).toString()\n> > -                                 << std::endl;\n> > +                       PR(\" * Pixelformat: {} {}\\n\", pixelformat,\n> > +                          formats.range(pixelformat).toString());\n> >\n> >                         for (const Size &size : formats.sizes(pixelformat))\n> > -                               std::cout << \"  - \" << size << std::endl;\n> > +                               PR(\"  - {}\\n\", size);\n> >                 }\n> >\n> >                 index++;\n> > @@ -168,7 +160,7 @@ int CameraSession::start()\n> >\n> >         ret = camera_->configure(config_.get());\n> >         if (ret < 0) {\n> > -               std::cout << \"Failed to configure camera\" << std::endl;\n> > +               PR(\"Failed to configure camera\\n\");\n> >                 return ret;\n> >         }\n> >\n> > @@ -197,8 +189,7 @@ int CameraSession::start()\n> >         if (sink_) {\n> >                 ret = sink_->configure(*config_);\n> >                 if (ret < 0) {\n> > -                       std::cout << \"Failed to configure frame sink\"\n> > -                                 << std::endl;\n> > +                       PR(\"Failed to configure frame sink\\n\");\n> >                         return ret;\n> >                 }\n> >\n> > @@ -214,12 +205,12 @@ void CameraSession::stop()\n> >  {\n> >         int ret = camera_->stop();\n> >         if (ret)\n> > -               std::cout << \"Failed to stop capture\" << std::endl;\n> > +               PR(\"Failed to stop capture\\n\");\n> >\n> >         if (sink_) {\n> >                 ret = sink_->stop();\n> >                 if (ret)\n> > -                       std::cout << \"Failed to stop frame sink\" <<\n> > std::endl;\n> > +                       PR(\"Failed to stop frame sink\\n\");\n> >         }\n> >\n> >         sink_.reset();\n> > @@ -238,7 +229,7 @@ int CameraSession::startCapture()\n> >         for (StreamConfiguration &cfg : *config_) {\n> >                 ret = allocator_->allocate(cfg.stream());\n> >                 if (ret < 0) {\n> > -                       std::cerr << \"Can't allocate buffers\" << std::endl;\n> > +                       EPR(\"Can't allocate buffers\\n\");\n> >                         return -ENOMEM;\n> >                 }\n> >\n> > @@ -254,7 +245,7 @@ int CameraSession::startCapture()\n> >         for (unsigned int i = 0; i < nbuffers; i++) {\n> >                 std::unique_ptr<Request> request =\n> > camera_->createRequest();\n> >                 if (!request) {\n> > -                       std::cerr << \"Can't create request\" << std::endl;\n> > +                       EPR(\"Can't create request\\n\");\n> >                         return -ENOMEM;\n> >                 }\n> >\n> > @@ -266,8 +257,7 @@ int CameraSession::startCapture()\n> >\n> >                         ret = request->addBuffer(stream, buffer.get());\n> >                         if (ret < 0) {\n> > -                               std::cerr << \"Can't set buffer for request\"\n> > -                                         << std::endl;\n> > +                               EPR(\"Can't set buffer for request\\n\");\n> >                                 return ret;\n> >                         }\n> >\n> > @@ -281,14 +271,14 @@ int CameraSession::startCapture()\n> >         if (sink_) {\n> >                 ret = sink_->start();\n> >                 if (ret) {\n> > -                       std::cout << \"Failed to start frame sink\" <<\n> > std::endl;\n> > +                       PR(\"Failed to start frame sink\\n\");\n> >                         return ret;\n> >                 }\n> >         }\n> >\n> >         ret = camera_->start();\n> >         if (ret) {\n> > -               std::cout << \"Failed to start capture\" << std::endl;\n> > +               PR(\"Failed to start capture\\n\");\n> >                 if (sink_)\n> >                         sink_->stop();\n> >                 return ret;\n> > @@ -297,7 +287,7 @@ int CameraSession::startCapture()\n> >         for (std::unique_ptr<Request> &request : requests_) {\n> >                 ret = queueRequest(request.get());\n> >                 if (ret < 0) {\n> > -                       std::cerr << \"Can't queue request\" << std::endl;\n> > +                       EPR(\"Can't queue request\\n\");\n> >                         camera_->stop();\n> >                         if (sink_)\n> >                                 sink_->stop();\n> > @@ -306,13 +296,11 @@ int CameraSession::startCapture()\n> >         }\n> >\n> >         if (captureLimit_)\n> > -               std::cout << \"cam\" << cameraIndex_\n> > -                         << \": Capture \" << captureLimit_ << \" frames\"\n> > -                         << std::endl;\n> > +               PR(\"cam{}: Capture {} frames\\n\", cameraIndex_,\n> > +                  captureLimit_);\n> >         else\n> > -               std::cout << \"cam\" << cameraIndex_\n> > -                         << \": Capture until user interrupts by SIGINT\"\n> > -                         << std::endl;\n> > +               PR(\"cam{}: Capture until user interrupts by SIGINT\\n\",\n> > +                  cameraIndex_);\n> >\n> >         return 0;\n> >  }\n> > @@ -364,23 +352,23 @@ void CameraSession::processRequest(Request *request)\n> >\n> >         bool requeue = true;\n> >\n> > -       std::stringstream info;\n> > -       info << ts / 1000000000 << \".\"\n> > -            << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000\n> > -            << \" (\" << std::fixed << std::setprecision(2) << fps << \"\n> > fps)\";\n> > +       auto sbuf = fmt::memory_buffer();\n> > +       fmt::format_to(std::back_inserter(sbuf), \"{}.{:06} ({:.2f} fps)\",\n> > +                      ts / 1000000000,\n> > +                      ts / 1000 % 1000000,\n> > +                      fps);\n> >\n> >         for (const auto &[stream, buffer] : buffers) {\n> >                 const FrameMetadata &metadata = buffer->metadata();\n> >\n> > -               info << \" \" << streamNames_[stream]\n> > -                    << \" seq: \" << std::setw(6) << std::setfill('0') <<\n> > metadata.sequence\n> > -                    << \" bytesused: \";\n> > +               fmt::format_to(std::back_inserter(sbuf), \" {} seq: {:06}\n> > bytesused: \",\n> > +                              streamNames_[stream], metadata.sequence);\n> >\n> >                 unsigned int nplane = 0;\n> >                 for (const FrameMetadata::Plane &plane :\n> > metadata.planes()) {\n> > -                       info << plane.bytesused;\n> > +                       fmt::format_to(std::back_inserter(sbuf), \"{}\",\n> > plane.bytesused);\n> >                         if (++nplane < metadata.planes().size())\n> > -                               info << \"/\";\n> > +                               fmt::format_to(std::back_inserter(sbuf),\n> > \"/\");\n> >                 }\n> >         }\n> >\n> > @@ -389,14 +377,13 @@ void CameraSession::processRequest(Request *request)\n> >                         requeue = false;\n> >         }\n> >\n> > -       std::cout << info.str() << std::endl;\n> > +       PR(\"{}\\n\", fmt::to_string(sbuf));\n> >\n> >         if (printMetadata_) {\n> >                 const ControlList &requestMetadata = request->metadata();\n> >                 for (const auto &[key, value] : requestMetadata) {\n> >                         const ControlId *id = controls::controls.at(key);\n> > -                       std::cout << \"\\t\" << id->name() << \" = \"\n> > -                                 << value.toString() << std::endl;\n> > +                       PR(\"\\t{} = {}\\n\", id->name(), value.toString());\n> >                 }\n> >         }\n> >\n> > diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp\n> > index 46e34eb5..84919ab3 100644\n> > --- a/src/cam/drm.cpp\n> > +++ b/src/cam/drm.cpp\n> > @@ -10,12 +10,12 @@\n> >  #include <algorithm>\n> >  #include <errno.h>\n> >  #include <fcntl.h>\n> > -#include <iostream>\n> >  #include <set>\n> >  #include <string.h>\n> >  #include <sys/ioctl.h>\n> >  #include <sys/stat.h>\n> >  #include <sys/types.h>\n> > +#include <fmt/core.h>\n> >\n> >  #include <libcamera/framebuffer.h>\n> >  #include <libcamera/geometry.h>\n> > @@ -25,6 +25,9 @@\n> >\n> >  #include \"event_loop.h\"\n> >\n> > +#define PR(...) fmt::print(__VA_ARGS__)\n> > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > +\n> >  namespace DRM {\n> >\n> >  Object::Object(Device *dev, uint32_t id, Type type)\n> > @@ -178,9 +181,7 @@ Connector::Connector(Device *dev, const\n> > drmModeConnector *connector)\n> >  {\n> >         auto typeName = connectorTypeNames.find(connector->connector_type);\n> >         if (typeName == connectorTypeNames.end()) {\n> > -               std::cerr\n> > -                       << \"Invalid connector type \"\n> > -                       << connector->connector_type << std::endl;\n> > +               EPR(\"Invalid connector type {}}n\",\n> > connector->connector_type);\n> >                 typeName =\n> > connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown);\n> >         }\n> >\n> > @@ -213,9 +214,7 @@ Connector::Connector(Device *dev, const\n> > drmModeConnector *connector)\n> >                                                     return e.id() ==\n> > encoderId;\n> >                                             });\n> >                 if (encoder == encoders.end()) {\n> > -                       std::cerr\n> > -                               << \"Encoder \" << encoderId << \" not found\"\n> > -                               << std::endl;\n> > +                       EPR(\"Encoder {} not found\\n\", encoderId);\n> >                         continue;\n> >                 }\n> >\n> > @@ -296,9 +295,7 @@ FrameBuffer::~FrameBuffer()\n> >\n> >                 if (ret == -1) {\n> >                         ret = -errno;\n> > -                       std::cerr\n> > -                               << \"Failed to close GEM object: \"\n> > -                               << strerror(-ret) << std::endl;\n> > +                       EPR(\"Failed to close GEM object: {}\\n\",\n> > strerror(-ret));\n> >                 }\n> >         }\n> >\n> > @@ -408,9 +405,8 @@ int Device::init()\n> >         fd_ = open(name, O_RDWR | O_CLOEXEC);\n> >         if (fd_ < 0) {\n> >                 ret = -errno;\n> > -               std::cerr\n> > -                       << \"Failed to open DRM/KMS device \" << name << \": \"\n> > -                       << strerror(-ret) << std::endl;\n> > +               EPR(\"Failed to open DRM/KMS device {}: {}\\n\", name,\n> > +                   strerror(-ret));\n> >                 return ret;\n> >         }\n> >\n> > @@ -421,9 +417,7 @@ int Device::init()\n> >         ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1);\n> >         if (ret < 0) {\n> >                 ret = -errno;\n> > -               std::cerr\n> > -                       << \"Failed to enable atomic capability: \"\n> > -                       << strerror(-ret) << std::endl;\n> > +               EPR(\"Failed to enable atomic capability: {}\\n\",\n> > strerror(-ret));\n> >                 return ret;\n> >         }\n> >\n> > @@ -448,9 +442,7 @@ int Device::getResources()\n> >         };\n> >         if (!resources) {\n> >                 ret = -errno;\n> > -               std::cerr\n> > -                       << \"Failed to get DRM/KMS resources: \"\n> > -                       << strerror(-ret) << std::endl;\n> > +               EPR(\"Failed to get DRM/KMS resources: {}\\n\",\n> > strerror(-ret));\n> >                 return ret;\n> >         }\n> >\n> > @@ -458,9 +450,7 @@ int Device::getResources()\n> >                 drmModeCrtc *crtc = drmModeGetCrtc(fd_,\n> > resources->crtcs[i]);\n> >                 if (!crtc) {\n> >                         ret = -errno;\n> > -                       std::cerr\n> > -                               << \"Failed to get CRTC: \" << strerror(-ret)\n> > -                               << std::endl;\n> > +                       EPR(\"Failed to get CRTC: {}\\n\", strerror(-ret));\n> >                         return ret;\n> >                 }\n> >\n> > @@ -476,9 +466,7 @@ int Device::getResources()\n> >                         drmModeGetEncoder(fd_, resources->encoders[i]);\n> >                 if (!encoder) {\n> >                         ret = -errno;\n> > -                       std::cerr\n> > -                               << \"Failed to get encoder: \" <<\n> > strerror(-ret)\n> > -                               << std::endl;\n> > +                       EPR(\"Failed to get encoder: {}\\n\", strerror(-ret));\n> >                         return ret;\n> >                 }\n> >\n> > @@ -494,9 +482,7 @@ int Device::getResources()\n> >                         drmModeGetConnector(fd_, resources->connectors[i]);\n> >                 if (!connector) {\n> >                         ret = -errno;\n> > -                       std::cerr\n> > -                               << \"Failed to get connector: \" <<\n> > strerror(-ret)\n> > -                               << std::endl;\n> > +                       EPR(\"Failed to get connector: {}\\n\",\n> > strerror(-ret));\n> >                         return ret;\n> >                 }\n> >\n> > @@ -513,9 +499,7 @@ int Device::getResources()\n> >         };\n> >         if (!planes) {\n> >                 ret = -errno;\n> > -               std::cerr\n> > -                       << \"Failed to get DRM/KMS planes: \"\n> > -                       << strerror(-ret) << std::endl;\n> > +               EPR(\"Failed to get DRM/KMS planes: {}\\n\", strerror(-ret));\n> >                 return ret;\n> >         }\n> >\n> > @@ -524,9 +508,7 @@ int Device::getResources()\n> >                         drmModeGetPlane(fd_, planes->planes[i]);\n> >                 if (!plane) {\n> >                         ret = -errno;\n> > -                       std::cerr\n> > -                               << \"Failed to get plane: \" <<\n> > strerror(-ret)\n> > -                               << std::endl;\n> > +                       EPR(\"Failed to get plane: {}\\n\", strerror(-ret));\n> >                         return ret;\n> >                 }\n> >\n> > @@ -556,9 +538,7 @@ int Device::getResources()\n> >                 drmModePropertyRes *property = drmModeGetProperty(fd_, id);\n> >                 if (!property) {\n> >                         ret = -errno;\n> > -                       std::cerr\n> > -                               << \"Failed to get property: \" <<\n> > strerror(-ret)\n> > -                               << std::endl;\n> > +                       EPR(\"Failed to get property: {}\\n\",\n> > strerror(-ret));\n> >                         continue;\n> >                 }\n> >\n> > @@ -573,9 +553,8 @@ int Device::getResources()\n> >         for (auto &object : objects_) {\n> >                 ret = object.second->setup();\n> >                 if (ret < 0) {\n> > -                       std::cerr\n> > -                               << \"Failed to setup object \" <<\n> > object.second->id()\n> > -                               << \": \" << strerror(-ret) << std::endl;\n> > +                       EPR(\"Failed to setup object {}: {}\\n\",\n> > +                           object.second->id(), strerror(-ret));\n> >                         return ret;\n> >                 }\n> >         }\n> > @@ -616,9 +595,8 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(\n> >                         ret = drmPrimeFDToHandle(fd_, plane.fd.get(),\n> > &handle);\n> >                         if (ret < 0) {\n> >                                 ret = -errno;\n> > -                               std::cerr\n> > -                                       << \"Unable to import framebuffer\n> > dmabuf: \"\n> > -                                       << strerror(-ret) << std::endl;\n> > +                               EPR(\"Unable to import framebuffer dmabuf:\n> > {}\\n\",\n> > +                                   strerror(-ret));\n> >                                 return nullptr;\n> >                         }\n> >\n> > @@ -636,9 +614,7 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(\n> >                             strides.data(), offsets, &fb->id_, 0);\n> >         if (ret < 0) {\n> >                 ret = -errno;\n> > -               std::cerr\n> > -                       << \"Failed to add framebuffer: \"\n> > -                       << strerror(-ret) << std::endl;\n> > +               EPR(\"Failed to add framebuffer: {}\\n\", strerror(-ret));\n> >                 return nullptr;\n> >         }\n> >\n> > diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp\n> > index e25784c0..87aaf59a 100644\n> > --- a/src/cam/event_loop.cpp\n> > +++ b/src/cam/event_loop.cpp\n> > @@ -10,7 +10,10 @@\n> >  #include <assert.h>\n> >  #include <event2/event.h>\n> >  #include <event2/thread.h>\n> > -#include <iostream>\n> > +#include <fmt/core.h>\n> > +\n> > +#define PR(...) fmt::print(__VA_ARGS__)\n> > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> >\n> >  EventLoop *EventLoop::instance_ = nullptr;\n> >\n> > @@ -71,13 +74,13 @@ void EventLoop::addEvent(int fd, EventType type,\n> >         event->event_ = event_new(base_, fd, events,\n> > &EventLoop::Event::dispatch,\n> >                                   event.get());\n> >         if (!event->event_) {\n> > -               std::cerr << \"Failed to create event for fd \" << fd <<\n> > std::endl;\n> > +               EPR(\"Failed to create event for fd {}\\n\", fd);\n> >                 return;\n> >         }\n> >\n> >         int ret = event_add(event->event_, nullptr);\n> >         if (ret < 0) {\n> > -               std::cerr << \"Failed to add event for fd \" << fd <<\n> > std::endl;\n> > +               EPR(\"Failed to add event for fd {}\\n\", fd);\n> >                 return;\n> >         }\n> >\n> > diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp\n> > index 45213d4a..86e2118c 100644\n> > --- a/src/cam/file_sink.cpp\n> > +++ b/src/cam/file_sink.cpp\n> > @@ -7,11 +7,12 @@\n> >\n> >  #include <assert.h>\n> >  #include <fcntl.h>\n> > -#include <iomanip>\n> > -#include <iostream>\n> > -#include <sstream>\n> >  #include <string.h>\n> >  #include <unistd.h>\n> > +#include <fmt/core.h>\n> > +\n> > +#define PR(...) fmt::print(__VA_ARGS__)\n> > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> >\n> >  #include <libcamera/camera.h>\n> >\n> > @@ -70,10 +71,10 @@ void FileSink::writeBuffer(const Stream *stream,\n> > FrameBuffer *buffer)\n> >\n> >         pos = filename.find_first_of('#');\n> >         if (pos != std::string::npos) {\n> > -               std::stringstream ss;\n> > -               ss << streamNames_[stream] << \"-\" << std::setw(6)\n> > -                  << std::setfill('0') << buffer->metadata().sequence;\n> > -               filename.replace(pos, 1, ss.str());\n> > +               auto s = fmt::format(\"{}-{:06}\",\n> > +                                    streamNames_[stream],\n> > +                                    buffer->metadata().sequence);\n> > +               filename.replace(pos, 1, s);\n> >         }\n> >\n> >         fd = open(filename.c_str(), O_CREAT | O_WRONLY |\n> > @@ -81,8 +82,7 @@ void FileSink::writeBuffer(const Stream *stream,\n> > FrameBuffer *buffer)\n> >                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |\n> > S_IWOTH);\n> >         if (fd == -1) {\n> >                 ret = -errno;\n> > -               std::cerr << \"failed to open file \" << filename << \": \"\n> > -                         << strerror(-ret) << std::endl;\n> > +               EPR(\"failed to open file {}: {}\\n\", filename,\n> > strerror(-ret));\n> >                 return;\n> >         }\n> >\n> > @@ -95,20 +95,17 @@ void FileSink::writeBuffer(const Stream *stream,\n> > FrameBuffer *buffer)\n> >                 unsigned int length = std::min<unsigned\n> > int>(meta.bytesused, data.size());\n> >\n> >                 if (meta.bytesused > data.size())\n> > -                       std::cerr << \"payload size \" << meta.bytesused\n> > -                                 << \" larger than plane size \" <<\n> > data.size()\n> > -                                 << std::endl;\n> > +                       EPR(\"payload size {} larger than plane size {}\\n\",\n> > +                           meta.bytesused, data.size());\n> >\n> >                 ret = ::write(fd, data.data(), length);\n> >                 if (ret < 0) {\n> >                         ret = -errno;\n> > -                       std::cerr << \"write error: \" << strerror(-ret)\n> > -                                 << std::endl;\n> > +                       EPR(\"write error: {}\\n\", strerror(-ret));\n> >                         break;\n> >                 } else if (ret != (int)length) {\n> > -                       std::cerr << \"write error: only \" << ret\n> > -                                 << \" bytes written instead of \"\n> > -                                 << length << std::endl;\n> > +                       EPR(\"write error: only {} bytes written instead of\n> > {}\\n\",\n> > +                           ret, length);\n> >                         break;\n> >                 }\n> >         }\n> > diff --git a/src/cam/image.cpp b/src/cam/image.cpp\n> > index fe2cc6da..73bcf915 100644\n> > --- a/src/cam/image.cpp\n> > +++ b/src/cam/image.cpp\n> > @@ -9,11 +9,14 @@\n> >\n> >  #include <assert.h>\n> >  #include <errno.h>\n> > -#include <iostream>\n> >  #include <map>\n> >  #include <string.h>\n> >  #include <sys/mman.h>\n> >  #include <unistd.h>\n> > +#include <fmt/core.h>\n> > +\n> > +#define PR(...) fmt::print(__VA_ARGS__)\n> > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> >\n> >  using namespace libcamera;\n> >\n> > @@ -49,10 +52,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const\n> > FrameBuffer *buffer, MapMode\n> >\n> >                 if (plane.offset > length ||\n> >                     plane.offset + plane.length > length) {\n> > -                       std::cerr << \"plane is out of buffer: buffer\n> > length=\"\n> > -                                 << length << \", plane offset=\" <<\n> > plane.offset\n> > -                                 << \", plane length=\" << plane.length\n> > -                                 << std::endl;\n> > +                       EPR(\"plane is out of buffer: buffer length={},\n> > plane offset={}, plane length={}\\n\",\n> > +                           length, plane.offset, plane.length);\n> >                         return nullptr;\n> >                 }\n> >                 size_t &mapLength = mappedBuffers[fd].mapLength;\n> > @@ -68,8 +69,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const\n> > FrameBuffer *buffer, MapMode\n> >                                              MAP_SHARED, fd, 0);\n> >                         if (address == MAP_FAILED) {\n> >                                 int error = -errno;\n> > -                               std::cerr << \"Failed to mmap plane: \"\n> > -                                         << strerror(-error) << std::endl;\n> > +                               EPR(\"Failed to mmap plane: {}\\n\",\n> > +                                   strerror(-error));\n> >                                 return nullptr;\n> >                         }\n> >\n> > diff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp\n> > index 7add81a6..823b75e4 100644\n> > --- a/src/cam/kms_sink.cpp\n> > +++ b/src/cam/kms_sink.cpp\n> > @@ -10,10 +10,13 @@\n> >  #include <array>\n> >  #include <algorithm>\n> >  #include <assert.h>\n> > -#include <iostream>\n> >  #include <memory>\n> >  #include <stdint.h>\n> >  #include <string.h>\n> > +#include <fmt/core.h>\n> > +\n> > +#define PR(...) fmt::print(__VA_ARGS__)\n> > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> >\n> >  #include <libcamera/camera.h>\n> >  #include <libcamera/formats.h>\n> > @@ -54,11 +57,9 @@ KMSSink::KMSSink(const std::string &connectorName)\n> >\n> >         if (!connector_) {\n> >                 if (!connectorName.empty())\n> > -                       std::cerr\n> > -                               << \"Connector \" << connectorName << \" not\n> > found\"\n> > -                               << std::endl;\n> > +                       EPR(\"Connector {} not found\\n\", connectorName);\n> >                 else\n> > -                       std::cerr << \"No connected connector found\" <<\n> > std::endl;\n> > +                       EPR(\"No connected connector found\\n\");\n> >                 return;\n> >         }\n> >\n> > @@ -119,7 +120,7 @@ int KMSSink::configure(const\n> > libcamera::CameraConfiguration &config)\n> >                                                       mode.vdisplay ==\n> > cfg.size.height;\n> >                                        });\n> >         if (iter == modes.end()) {\n> > -               std::cerr << \"No mode matching \" << cfg.size << std::endl;\n> > +               EPR(\"No mode matching {}\\n\", cfg.size);\n> >                 return -EINVAL;\n> >         }\n> >\n> > @@ -192,17 +193,12 @@ int KMSSink::configurePipeline(const\n> > libcamera::PixelFormat &format)\n> >  {\n> >         const int ret = selectPipeline(format);\n> >         if (ret) {\n> > -               std::cerr\n> > -                       << \"Unable to find display pipeline for format \"\n> > -                       << format << std::endl;\n> > -\n> > +               EPR(\"Unable to find display pipeline for format {}\\n\",\n> > format);\n> >                 return ret;\n> >         }\n> >\n> > -       std::cout\n> > -               << \"Using KMS plane \" << plane_->id() << \", CRTC \" <<\n> > crtc_->id()\n> > -               << \", connector \" << connector_->name()\n> > -               << \" (\" << connector_->id() << \")\" << std::endl;\n> > +       PR(\"Using KMS plane {}, CRTC {}, connector {} ({})\\n\",\n> > +          plane_->id(), crtc_->id(), connector_->name(),\n> > connector_->id());\n> >\n> >         return 0;\n> >  }\n> > @@ -228,9 +224,8 @@ int KMSSink::start()\n> >\n> >         ret = request->commit(DRM::AtomicRequest::FlagAllowModeset);\n> >         if (ret < 0) {\n> > -               std::cerr\n> > -                       << \"Failed to disable CRTCs and planes: \"\n> > -                       << strerror(-ret) << std::endl;\n> > +               EPR(\"Failed to disable CRTCs and planes: {}\\n\",\n> > +                   strerror(-ret));\n> >                 return ret;\n> >         }\n> >\n> > @@ -250,9 +245,7 @@ int KMSSink::stop()\n> >\n> >         int ret = request.commit(DRM::AtomicRequest::FlagAllowModeset);\n> >         if (ret < 0) {\n> > -               std::cerr\n> > -                       << \"Failed to stop display pipeline: \"\n> > -                       << strerror(-ret) << std::endl;\n> > +               EPR(\"Failed to stop display pipeline: {}\\n\",\n> > strerror(-ret));\n> >                 return ret;\n> >         }\n> >\n> > @@ -312,9 +305,8 @@ bool KMSSink::processRequest(libcamera::Request\n> > *camRequest)\n> >         if (!queued_) {\n> >                 int ret = drmRequest->commit(flags);\n> >                 if (ret < 0) {\n> > -                       std::cerr\n> > -                               << \"Failed to commit atomic request: \"\n> > -                               << strerror(-ret) << std::endl;\n> > +                       EPR(\"Failed to commit atomic request: {}\\n\",\n> > +                           strerror(-ret));\n> >                         /* \\todo Implement error handling */\n> >                 }\n> >\n> > diff --git a/src/cam/main.cpp b/src/cam/main.cpp\n> > index c7f664b9..03615dc9 100644\n> > --- a/src/cam/main.cpp\n> > +++ b/src/cam/main.cpp\n> > @@ -6,10 +6,9 @@\n> >   */\n> >\n> >  #include <atomic>\n> > -#include <iomanip>\n> > -#include <iostream>\n> >  #include <signal.h>\n> >  #include <string.h>\n> > +#include <fmt/core.h>\n> >\n> >  #include <libcamera/libcamera.h>\n> >  #include <libcamera/property_ids.h>\n> > @@ -78,8 +77,7 @@ int CamApp::init(int argc, char **argv)\n> >\n> >         ret = cm_->start();\n> >         if (ret) {\n> > -               std::cout << \"Failed to start camera manager: \"\n> > -                         << strerror(-ret) << std::endl;\n> > +               fmt::print(\"Failed to start camera manager: {}\\n\", -ret);\n> >                 return ret;\n> >         }\n> >\n> > @@ -173,12 +171,12 @@ int CamApp::parseOptions(int argc, char *argv[])\n> >\n> >  void CamApp::cameraAdded(std::shared_ptr<Camera> cam)\n> >  {\n> > -       std::cout << \"Camera Added: \" << cam->id() << std::endl;\n> > +       fmt::print(\"Camera Added: {}\\n\", cam->id());\n> >  }\n> >\n> >  void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n> >  {\n> > -       std::cout << \"Camera Removed: \" << cam->id() << std::endl;\n> > +       fmt::print(\"Camera Removed: {}\\n\", cam->id());\n> >  }\n> >\n> >  void CamApp::captureDone()\n> > @@ -193,11 +191,11 @@ int CamApp::run()\n> >\n> >         /* 1. List all cameras. */\n> >         if (options_.isSet(OptList)) {\n> > -               std::cout << \"Available cameras:\" << std::endl;\n> > +               fmt::print(\"Available cameras:\\n\");\n> >\n> >                 unsigned int index = 1;\n> >                 for (const std::shared_ptr<Camera> &cam : cm_->cameras()) {\n> > -                       std::cout << index << \": \" <<\n> > cameraName(cam.get()) << std::endl;\n> > +                       fmt::print(\"{}: {}\\n\", cameraName(cam.get()),\n> > index);\n> >                         index++;\n> >                 }\n> >         }\n> > @@ -215,12 +213,12 @@ int CamApp::run()\n> >                                                                 index,\n> >\n> > camera.children());\n> >                         if (!session->isValid()) {\n> > -                               std::cout << \"Failed to create camera\n> > session\" << std::endl;\n> > +                               fmt::print(\"Failed to create camera\n> > session\\n\");\n> >                                 return -EINVAL;\n> >                         }\n> >\n> > -                       std::cout << \"Using camera \" <<\n> > session->camera()->id()\n> > -                                 << \" as cam\" << index << std::endl;\n> > +                       fmt::print(\"Using camera{} as cam{}\\n\",\n> > +                                  session->camera()->id(), index);\n> >\n> >                         session->captureDone.connect(this,\n> > &CamApp::captureDone);\n> >\n> > @@ -250,7 +248,7 @@ int CamApp::run()\n> >\n> >                 ret = session->start();\n> >                 if (ret) {\n> > -                       std::cout << \"Failed to start camera session\" <<\n> > std::endl;\n> > +                       fmt::print(\"Failed to start camera session\\n\");\n> >                         return ret;\n> >                 }\n> >\n> > @@ -259,8 +257,8 @@ int CamApp::run()\n> >\n> >         /* 5. Enable hotplug monitoring. */\n> >         if (options_.isSet(OptMonitor)) {\n> > -               std::cout << \"Monitoring new hotplug and unplug events\" <<\n> > std::endl;\n> > -               std::cout << \"Press Ctrl-C to interrupt\" << std::endl;\n> > +               fmt::print(\"Monitoring new hotplug and unplug events\\n\");\n> > +               fmt::print(\"Press Ctrl-C to interrupt\\n\");\n> >\n> >                 cm_->cameraAdded.connect(this, &CamApp::cameraAdded);\n> >                 cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);\n> > @@ -323,7 +321,7 @@ std::string CamApp::cameraName(const Camera *camera)\n> >\n> >  void signalHandler([[maybe_unused]] int signal)\n> >  {\n> > -       std::cout << \"Exiting\" << std::endl;\n> > +       fmt::print(\"Exiting\");\n> >         CamApp::instance()->quit();\n> >  }\n> >\n> > diff --git a/src/cam/meson.build b/src/cam/meson.build\n> > index 5bab8c9e..2b47383d 100644\n> > --- a/src/cam/meson.build\n> > +++ b/src/cam/meson.build\n> > @@ -7,6 +7,8 @@ if not libevent.found()\n> >      subdir_done()\n> >  endif\n> >\n> > +libfmt_dep = dependency('fmt')\n> > +\n> >  cam_enabled = true\n> >\n> >  cam_sources = files([\n> > @@ -25,7 +27,7 @@ cam_cpp_args = []\n> >  libdrm = dependency('libdrm', required : false)\n> >\n> >  if libdrm.found()\n> > -    cam_cpp_args += [ '-DHAVE_KMS' ]\n> > +    cam_cpp_args += [ '-DHAVE_KMS', ]\n> >      cam_sources += files([\n> >          'drm.cpp',\n> >          'kms_sink.cpp'\n> > @@ -38,6 +40,7 @@ cam  = executable('cam', cam_sources,\n> >                        libcamera_public,\n> >                        libdrm,\n> >                        libevent,\n> > +                      libfmt_dep,\n> >                    ],\n> >                    cpp_args : cam_cpp_args,\n> >                    install : true)\n> > diff --git a/src/cam/options.cpp b/src/cam/options.cpp\n> > index 4f7e8691..c9979385 100644\n> > --- a/src/cam/options.cpp\n> > +++ b/src/cam/options.cpp\n> > @@ -7,9 +7,11 @@\n> >\n> >  #include <assert.h>\n> >  #include <getopt.h>\n> > -#include <iomanip>\n> > -#include <iostream>\n> >  #include <string.h>\n> > +#include <fmt/core.h>\n> > +\n> > +#define PR(...) fmt::print(__VA_ARGS__)\n> > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> >\n> >  #include \"options.h\"\n> >\n> > @@ -390,26 +392,23 @@ KeyValueParser::Options KeyValueParser::parse(const\n> > char *arguments)\n> >                         continue;\n> >\n> >                 if (optionsMap_.find(key) == optionsMap_.end()) {\n> > -                       std::cerr << \"Invalid option \" << key << std::endl;\n> > +                       EPR(\"Invalid option {}\\n\", key);\n> >                         return options;\n> >                 }\n> >\n> >                 OptionArgument arg = optionsMap_[key].argument;\n> >                 if (value.empty() && arg == ArgumentRequired) {\n> > -                       std::cerr << \"Option \" << key << \" requires an\n> > argument\"\n> > -                                 << std::endl;\n> > +                       EPR(\"Option {} requires an argument\\n\", key);\n> >                         return options;\n> >                 } else if (!value.empty() && arg == ArgumentNone) {\n> > -                       std::cerr << \"Option \" << key << \" takes no\n> > argument\"\n> > -                                 << std::endl;\n> > +                       EPR(\"Option {} takes no argument\\n\", key);\n> >                         return options;\n> >                 }\n> >\n> >                 const Option &option = optionsMap_[key];\n> >                 if (!options.parseValue(key, option, value.c_str())) {\n> > -                       std::cerr << \"Failed to parse '\" << value << \"' as\n> > \"\n> > -                                 << option.typeName() << \" for option \"\n> > << key\n> > -                                 << std::endl;\n> > +                       EPR(\"Failed to parse '{}' as {} for option {}\\n\",\n> > +                           value, option.typeName(), key);\n> >                         return options;\n> >                 }\n> >         }\n> > @@ -453,16 +452,16 @@ void KeyValueParser::usage(int indent)\n> >                                 argument += \"]\";\n> >                 }\n> >\n> > -               std::cerr << std::setw(indent) << argument;\n> > +               EPR(\"{:{}}\", argument, indent);\n> >\n> >                 for (const char *help = option.help, *end = help; end;) {\n> >                         end = strchr(help, '\\n');\n> >                         if (end) {\n> > -                               std::cerr << std::string(help, end - help\n> > + 1);\n> > -                               std::cerr << std::setw(indent) << \" \";\n> > +                               EPR(std::string(help, end - help + 1));\n> > +                               EPR(\"{:{}}\", \"\", indent);\n> >                                 help = end + 1;\n> >                         } else {\n> > -                               std::cerr << help << std::endl;\n> > +                               EPR(\"{}\\n\", help);\n> >                         }\n> >                 }\n> >         }\n> > @@ -929,10 +928,10 @@ OptionsParser::Options OptionsParser::parse(int\n> > argc, char **argv)\n> >\n> >                 if (c == '?' || c == ':') {\n> >                         if (c == '?')\n> > -                               std::cerr << \"Invalid option \";\n> > +                               EPR(\"Invalid option \");\n> >                         else\n> > -                               std::cerr << \"Missing argument for option\n> > \";\n> > -                       std::cerr << argv[optind - 1] << std::endl;\n> > +                               EPR(\"Missing argument for option \");\n> > +                       EPR(\"{}\\n\", argv[optind - 1]);\n> >\n> >                         usage();\n> >                         return options;\n> > @@ -946,8 +945,7 @@ OptionsParser::Options OptionsParser::parse(int argc,\n> > char **argv)\n> >         }\n> >\n> >         if (optind < argc) {\n> > -               std::cerr << \"Invalid non-option argument '\" <<\n> > argv[optind]\n> > -                         << \"'\" << std::endl;\n> > +               EPR(\"Invalid non-option argument '{}'\\n\", argv[optind]);\n> >                 usage();\n> >                 return options;\n> >         }\n> > @@ -992,14 +990,9 @@ void OptionsParser::usage()\n> >\n> >         indent = (indent + 7) / 8 * 8;\n> >\n> > -       std::cerr << \"Options:\" << std::endl;\n> > -\n> > -       std::ios_base::fmtflags f(std::cerr.flags());\n> > -       std::cerr << std::left;\n> > +       EPR(\"Options:\\n\");\n> >\n> >         usageOptions(options_, indent);\n> > -\n> > -       std::cerr.flags(f);\n> >  }\n> >\n> >  void OptionsParser::usageOptions(const std::list<Option> &options,\n> > @@ -1036,16 +1029,16 @@ void OptionsParser::usageOptions(const\n> > std::list<Option> &options,\n> >                 if (option.isArray)\n> >                         argument += \" ...\";\n> >\n> > -               std::cerr << std::setw(indent) << argument;\n> > +               EPR(\"{:{}}\", argument, indent);\n> >\n> > -               for (const char *help = option.help, *end = help; end; ) {\n> > +               for (const char *help = option.help, *end = help; end;) {\n> >                         end = strchr(help, '\\n');\n> >                         if (end) {\n> > -                               std::cerr << std::string(help, end - help\n> > + 1);\n> > -                               std::cerr << std::setw(indent) << \" \";\n> > +                               EPR(std::string(help, end - help + 1));\n> > +                               EPR(\"{:{}}\", \"\", indent);\n> >                                 help = end + 1;\n> >                         } else {\n> > -                               std::cerr << help << std::endl;\n> > +                               EPR(\"{}\\n\", help);\n> >                         }\n> >                 }\n> >\n> > @@ -1060,8 +1053,8 @@ void OptionsParser::usageOptions(const\n> > std::list<Option> &options,\n> >                 return;\n> >\n> >         for (const Option *option : parentOptions) {\n> > -               std::cerr << std::endl << \"Options valid in the context of\n> > \"\n> > -                         << option->optionName() << \":\" << std::endl;\n> > +               EPR(\"\\nOptions valid in the context of {}:\\n\",\n> > +                   option->optionName());\n> >                 usageOptions(option->children, indent);\n> >         }\n> >  }\n> > @@ -1125,15 +1118,14 @@ bool OptionsParser::parseValue(const Option\n> > &option, const char *arg,\n> >\n> >         std::tie(options, error) = childOption(option.parent, options);\n> >         if (error) {\n> > -               std::cerr << \"Option \" << option.optionName() << \"\n> > requires a \"\n> > -                         << error->optionName() << \" context\" <<\n> > std::endl;\n> > +               EPR(\"Option {} requires a {} context\\n\",\n> > +                   option.optionName(), error->optionName());\n> >                 return false;\n> >         }\n> >\n> >         if (!options->parseValue(option.opt, option, arg)) {\n> > -               std::cerr << \"Can't parse \" << option.typeName()\n> > -                         << \" argument for option \" << option.optionName()\n> > -                         << std::endl;\n> > +               EPR(\"Can't parse {} argument for option {}\\n\",\n> > +                   option.typeName(), option.optionName());\n> >                 return false;\n> >         }\n> >\n> > diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp\n> > index 150bd27c..666862eb 100644\n> > --- a/src/cam/stream_options.cpp\n> > +++ b/src/cam/stream_options.cpp\n> > @@ -6,7 +6,10 @@\n> >   */\n> >  #include \"stream_options.h\"\n> >\n> > -#include <iostream>\n> > +#include <fmt/core.h>\n> > +\n> > +#define PR(...) fmt::print(__VA_ARGS__)\n> > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> >\n> >  using namespace libcamera;\n> >\n> > @@ -30,8 +33,7 @@ KeyValueParser::Options\n> > StreamKeyValueParser::parse(const char *arguments)\n> >\n> >         if (options.valid() && options.isSet(\"role\") &&\n> >             !parseRole(&role, options)) {\n> > -               std::cerr << \"Unknown stream role \"\n> > -                         << options[\"role\"].toString() << std::endl;\n> > +               EPR(\"Unknown stream role {}\\n\",\n> > options[\"role\"].toString());\n> >                 options.invalidate();\n> >         }\n> >\n> > @@ -64,7 +66,7 @@ int\n> > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,\n> >                                               const OptionValue &values)\n> >  {\n> >         if (!config) {\n> > -               std::cerr << \"No configuration provided\" << std::endl;\n> > +               EPR(\"No configuration provided\\n\");\n> >                 return -EINVAL;\n> >         }\n> >\n> > @@ -75,12 +77,8 @@ int\n> > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,\n> >         const std::vector<OptionValue> &streamParameters =\n> > values.toArray();\n> >\n> >         if (config->size() != streamParameters.size()) {\n> > -               std::cerr\n> > -                       << \"Number of streams in configuration \"\n> > -                       << config->size()\n> > -                       << \" does not match number of streams parsed \"\n> > -                       << streamParameters.size()\n> > -                       << std::endl;\n> > +               EPR(\"Number of streams in configuration {} does not match\n> > number of streams parsed {}\\n\",\n> > +                   config->size(), streamParameters.size());\n> >                 return -EINVAL;\n> >         }\n> >\n> > --\n> > 2.34.1\n> >\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 9328DC0F2A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 10:09:57 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F0DB265645;\n\tTue, 10 May 2022 12:09:55 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 7F49465643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 12:09:54 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id E63095A4;\n\tTue, 10 May 2022 12:09:53 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652177396;\n\tbh=KlRKDfpUR7ZqNKR+gDcrGxTHM2+yZ1GSdQq3Ff3JYKY=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=FoGebtArLy1RzrobjgechD7eKfCjr7vNKU2S8lUtW3i4n9W49f5y3i5ZDy4W9NxzX\n\tibYHiKw4BR0wrOyF8r6p7Ud4wPCLd4wewQXrLjjiJa/fe7wsnDO/uzTNkMXsDOr88U\n\tmW5+MsWmLjsDCOi6ZORPT2LhTkN5zea+QwOYNXbz/vmeYElxCPBFWhwElODOMVumNR\n\teZbPtDcZFDtj+rg3TXnbVqnfEIYaOaipEiPMKMTK7tGdAheinphVmZ2D2IDXqtJ3S2\n\tbwbNFiNlh3fGPVFxRnebYXGFPH/QwurQnK1gXwpieohd2HixQto9mWhLtQQxrvbSoX\n\tW7EUSCiX7eZbA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652177394;\n\tbh=KlRKDfpUR7ZqNKR+gDcrGxTHM2+yZ1GSdQq3Ff3JYKY=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=oZmf4nl2krWdGAqtJYrzvENWI3WesMGgpryWs1Kz1tHqcQcDJN2ZdqkT4ec+QnpE2\n\t163VRjE6x4aiHXrhrCfpQqt8WfpeCeYFbF0cYUxPSb4Ek/+N9zCFe493I6QjNSU99O\n\t1vd1jnSiNDR+KAjaSxodb4yeD9cTezEMWar6HIHc="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"oZmf4nl2\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>","To":"Naushir Patuck <naush@raspberrypi.com>,\n\tTomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Date":"Tue, 10 May 2022 11:09:51 +0100","Message-ID":"<165217739174.2416244.12652784382118148891@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22932,"web_url":"https://patchwork.libcamera.org/comment/22932/","msgid":"<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>","date":"2022-05-10T12:59:37","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":101,"url":"https://patchwork.libcamera.org/api/people/101/","name":"Eric Curtin","email":"ecurtin@redhat.com"},"content":"On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel\n<libcamera-devel@lists.libcamera.org> wrote:\n>\n> Quoting Naushir Patuck (2022-05-10 10:49:14)\n> > Hi Tomi,\n> >\n> > On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel <\n> > libcamera-devel@lists.libcamera.org> wrote:\n> >\n> > > This is just a conversation starter, not for merging. I really like\n> > > libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > > is the first library I add to any new C++ project I make.\n> > >\n> > > You can find more information about libfmt from:\n> > >\n> > > https://github.com/fmtlib/fmt\n> > > https://fmt.dev/latest/index.html\n> > >\n> > > This patch is just a crude conversion with ugly macros to showcase what\n> > > the formatting code might look like.\n> > >\n> > > libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > > than iostreams, but for the size it didn't seem to be true in cam's case\n> > > as the tests below show. However, simple prints did reduce the exe size,\n> > > but the few more complex ones increased the size.\n> > >\n> > > Size tests with gcc 11.2.0-19ubuntu1\n> > >\n> > > - Without libfmt\n> > >\n> > > debug           3523400\n> > > debug lto       3269368\n> > > release         223056\n> > > release lto     172280\n> > >\n> > > - With libfmt\n> > >\n> > > debug           4424256\n> > > debug lto       4143840\n> > > release         303952\n> > > release lto     252640\n> > >\n> > > Above shows that cam's size clearly increases with libfmt. However, the\n> > > increase really comes only from one case, the use of fmt::memory_buffer\n> > > and std::back_inserter. Converting that code to use fmt::format() and\n> > > naive string append gives:\n> > >\n> > > release with string append      233680\n> > > release lto with string append  186936\n> > >\n> > > Also, if I add another use of fmt::memory_buffer and std::back_inserter\n> > > to another file, I see much less increase in the size:\n> > >\n> > > release lto with two uses of memory_buffer, back_inserter       256736\n> > >\n> > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> > >\n> >\n> > For what it's worth, I absolutely loathe formatting in std iostream, and\n> > libfmt is a wonderful alternative.\n> > It also is close enough to the C++20 std::format implementation that\n> > eventual porting would be low effort.  So I am all for this change :)\n\nI am all in for this change also, personally I would have changed to\nprintf for now\nto have one less dependency (also an easy port to C++20 std::format). The\ndependency list is getting large, I noticed a build of mine failing\nrecently because\nI didn't have libyaml.\n\nBut std::format and libfmt are quite fast and anything is better than\nstreams so +1.\n\n>\n> I've never used (yet) {fmt}, but I've only heard good things about\n> performance, and of course it's headed into the standard, so I also\n> think there is some good merit to be found in this development.\n>\n> --\n> Kieran\n>\n>\n> >\n> > Regards,\n> > Naush\n> >\n> >\n> >\n> > > ---\n> > >  src/cam/camera_session.cpp | 105 ++++++++++++++++---------------------\n> > >  src/cam/drm.cpp            |  68 ++++++++----------------\n> > >  src/cam/event_loop.cpp     |   9 ++--\n> > >  src/cam/file_sink.cpp      |  31 +++++------\n> > >  src/cam/image.cpp          |  15 +++---\n> > >  src/cam/kms_sink.cpp       |  38 ++++++--------\n> > >  src/cam/main.cpp           |  28 +++++-----\n> > >  src/cam/meson.build        |   5 +-\n> > >  src/cam/options.cpp        |  66 ++++++++++-------------\n> > >  src/cam/stream_options.cpp |  18 +++----\n> > >  10 files changed, 165 insertions(+), 218 deletions(-)\n> > >\n> > > diff --git a/src/cam/camera_session.cpp b/src/cam/camera_session.cpp\n> > > index efffafbf..7843c3fd 100644\n> > > --- a/src/cam/camera_session.cpp\n> > > +++ b/src/cam/camera_session.cpp\n> > > @@ -5,10 +5,9 @@\n> > >   * camera_session.cpp - Camera capture session\n> > >   */\n> > >\n> > > -#include <iomanip>\n> > > -#include <iostream>\n> > >  #include <limits.h>\n> > > -#include <sstream>\n> > > +#include <fmt/format.h>\n> > > +#include <fmt/ostream.h>\n> > >\n> > >  #include <libcamera/control_ids.h>\n> > >  #include <libcamera/property_ids.h>\n> > > @@ -22,6 +21,9 @@\n> > >  #include \"main.h\"\n> > >  #include \"stream_options.h\"\n> > >\n> > > +#define PR(...) fmt::print(__VA_ARGS__)\n> > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > > +\n> > >  using namespace libcamera;\n> > >\n> > >  CameraSession::CameraSession(CameraManager *cm,\n> > > @@ -40,13 +42,12 @@ CameraSession::CameraSession(CameraManager *cm,\n> > >                 camera_ = cm->get(cameraId);\n> > >\n> > >         if (!camera_) {\n> > > -               std::cerr << \"Camera \" << cameraId << \" not found\" <<\n> > > std::endl;\n> > > +               EPR(\"Camera {} not found\\n\", cameraId);\n> > >                 return;\n> > >         }\n> > >\n> > >         if (camera_->acquire()) {\n> > > -               std::cerr << \"Failed to acquire camera \" << cameraId\n> > > -                         << std::endl;\n> > > +               EPR(\"Failed to acquire camera {}\", cameraId);\n> > >                 return;\n> > >         }\n> > >\n> > > @@ -55,15 +56,14 @@ CameraSession::CameraSession(CameraManager *cm,\n> > >         std::unique_ptr<CameraConfiguration> config =\n> > >                 camera_->generateConfiguration(roles);\n> > >         if (!config || config->size() != roles.size()) {\n> > > -               std::cerr << \"Failed to get default stream configuration\"\n> > > -                         << std::endl;\n> > > +               EPR(\"Failed to get default stream configuration\\n\");\n> > >                 return;\n> > >         }\n> > >\n> > >         /* Apply configuration if explicitly requested. */\n> > >         if (StreamKeyValueParser::updateConfiguration(config.get(),\n> > >\n> > > options_[OptStream])) {\n> > > -               std::cerr << \"Failed to update configuration\" << std::endl;\n> > > +               EPR(\"Failed to update configuration\\n\");\n> > >                 return;\n> > >         }\n> > >\n> > > @@ -72,20 +72,17 @@ CameraSession::CameraSession(CameraManager *cm,\n> > >  #ifdef HAVE_KMS\n> > >         if (options_.isSet(OptDisplay)) {\n> > >                 if (options_.isSet(OptFile)) {\n> > > -                       std::cerr << \"--display and --file options are\n> > > mutually exclusive\"\n> > > -                                 << std::endl;\n> > > +                       EPR(\"--display and --file options are mutually\n> > > exclusive\\n\");\n> > >                         return;\n> > >                 }\n> > >\n> > >                 if (roles.size() != 1) {\n> > > -                       std::cerr << \"Display doesn't support multiple\n> > > streams\"\n> > > -                                 << std::endl;\n> > > +                       EPR(\"Display doesn't support multiple streams\\n\");\n> > >                         return;\n> > >                 }\n> > >\n> > >                 if (roles[0] != StreamRole::Viewfinder) {\n> > > -                       std::cerr << \"Display requires a viewfinder stream\"\n> > > -                                 << std::endl;\n> > > +                       EPR(\"Display requires a viewfinder stream\\n\");\n> > >                         return;\n> > >                 }\n> > >         }\n> > > @@ -97,15 +94,14 @@ CameraSession::CameraSession(CameraManager *cm,\n> > >\n> > >         case CameraConfiguration::Adjusted:\n> > >                 if (strictFormats) {\n> > > -                       std::cout << \"Adjusting camera configuration\n> > > disallowed by --strict-formats argument\"\n> > > -                                 << std::endl;\n> > > +                       PR(\"Adjusting camera configuration disallowed by\n> > > --strict-formats argument\\n\");\n> > >                         return;\n> > >                 }\n> > > -               std::cout << \"Camera configuration adjusted\" << std::endl;\n> > > +               PR(\"Camera configuration adjusted\\n\");\n> > >                 break;\n> > >\n> > >         case CameraConfiguration::Invalid:\n> > > -               std::cout << \"Camera configuration invalid\" << std::endl;\n> > > +               PR(\"Camera configuration invalid\\n\");\n> > >                 return;\n> > >         }\n> > >\n> > > @@ -121,8 +117,7 @@ CameraSession::~CameraSession()\n> > >  void CameraSession::listControls() const\n> > >  {\n> > >         for (const auto &[id, info] : camera_->controls()) {\n> > > -               std::cout << \"Control: \" << id->name() << \": \"\n> > > -                         << info.toString() << std::endl;\n> > > +               PR(\"Control: {}: {}}n\", id->name(), info.toString());\n> > >         }\n> > >  }\n> > >\n> > > @@ -131,8 +126,7 @@ void CameraSession::listProperties() const\n> > >         for (const auto &[key, value] : camera_->properties()) {\n> > >                 const ControlId *id = properties::properties.at(key);\n> > >\n> > > -               std::cout << \"Property: \" << id->name() << \" = \"\n> > > -                         << value.toString() << std::endl;\n> > > +               PR(\"Property: {} = {}\\n\", id->name(), value.toString());\n> > >         }\n> > >  }\n> > >\n> > > @@ -140,17 +134,15 @@ void CameraSession::infoConfiguration() const\n> > >  {\n> > >         unsigned int index = 0;\n> > >         for (const StreamConfiguration &cfg : *config_) {\n> > > -               std::cout << index << \": \" << cfg.toString() << std::endl;\n> > > +               PR(\"{}: {}\\n\", index, cfg.toString());\n> > >\n> > >                 const StreamFormats &formats = cfg.formats();\n> > >                 for (PixelFormat pixelformat : formats.pixelformats()) {\n> > > -                       std::cout << \" * Pixelformat: \"\n> > > -                                 << pixelformat << \" \"\n> > > -                                 << formats.range(pixelformat).toString()\n> > > -                                 << std::endl;\n> > > +                       PR(\" * Pixelformat: {} {}\\n\", pixelformat,\n> > > +                          formats.range(pixelformat).toString());\n> > >\n> > >                         for (const Size &size : formats.sizes(pixelformat))\n> > > -                               std::cout << \"  - \" << size << std::endl;\n> > > +                               PR(\"  - {}\\n\", size);\n> > >                 }\n> > >\n> > >                 index++;\n> > > @@ -168,7 +160,7 @@ int CameraSession::start()\n> > >\n> > >         ret = camera_->configure(config_.get());\n> > >         if (ret < 0) {\n> > > -               std::cout << \"Failed to configure camera\" << std::endl;\n> > > +               PR(\"Failed to configure camera\\n\");\n> > >                 return ret;\n> > >         }\n> > >\n> > > @@ -197,8 +189,7 @@ int CameraSession::start()\n> > >         if (sink_) {\n> > >                 ret = sink_->configure(*config_);\n> > >                 if (ret < 0) {\n> > > -                       std::cout << \"Failed to configure frame sink\"\n> > > -                                 << std::endl;\n> > > +                       PR(\"Failed to configure frame sink\\n\");\n> > >                         return ret;\n> > >                 }\n> > >\n> > > @@ -214,12 +205,12 @@ void CameraSession::stop()\n> > >  {\n> > >         int ret = camera_->stop();\n> > >         if (ret)\n> > > -               std::cout << \"Failed to stop capture\" << std::endl;\n> > > +               PR(\"Failed to stop capture\\n\");\n> > >\n> > >         if (sink_) {\n> > >                 ret = sink_->stop();\n> > >                 if (ret)\n> > > -                       std::cout << \"Failed to stop frame sink\" <<\n> > > std::endl;\n> > > +                       PR(\"Failed to stop frame sink\\n\");\n> > >         }\n> > >\n> > >         sink_.reset();\n> > > @@ -238,7 +229,7 @@ int CameraSession::startCapture()\n> > >         for (StreamConfiguration &cfg : *config_) {\n> > >                 ret = allocator_->allocate(cfg.stream());\n> > >                 if (ret < 0) {\n> > > -                       std::cerr << \"Can't allocate buffers\" << std::endl;\n> > > +                       EPR(\"Can't allocate buffers\\n\");\n> > >                         return -ENOMEM;\n> > >                 }\n> > >\n> > > @@ -254,7 +245,7 @@ int CameraSession::startCapture()\n> > >         for (unsigned int i = 0; i < nbuffers; i++) {\n> > >                 std::unique_ptr<Request> request =\n> > > camera_->createRequest();\n> > >                 if (!request) {\n> > > -                       std::cerr << \"Can't create request\" << std::endl;\n> > > +                       EPR(\"Can't create request\\n\");\n> > >                         return -ENOMEM;\n> > >                 }\n> > >\n> > > @@ -266,8 +257,7 @@ int CameraSession::startCapture()\n> > >\n> > >                         ret = request->addBuffer(stream, buffer.get());\n> > >                         if (ret < 0) {\n> > > -                               std::cerr << \"Can't set buffer for request\"\n> > > -                                         << std::endl;\n> > > +                               EPR(\"Can't set buffer for request\\n\");\n> > >                                 return ret;\n> > >                         }\n> > >\n> > > @@ -281,14 +271,14 @@ int CameraSession::startCapture()\n> > >         if (sink_) {\n> > >                 ret = sink_->start();\n> > >                 if (ret) {\n> > > -                       std::cout << \"Failed to start frame sink\" <<\n> > > std::endl;\n> > > +                       PR(\"Failed to start frame sink\\n\");\n> > >                         return ret;\n> > >                 }\n> > >         }\n> > >\n> > >         ret = camera_->start();\n> > >         if (ret) {\n> > > -               std::cout << \"Failed to start capture\" << std::endl;\n> > > +               PR(\"Failed to start capture\\n\");\n> > >                 if (sink_)\n> > >                         sink_->stop();\n> > >                 return ret;\n> > > @@ -297,7 +287,7 @@ int CameraSession::startCapture()\n> > >         for (std::unique_ptr<Request> &request : requests_) {\n> > >                 ret = queueRequest(request.get());\n> > >                 if (ret < 0) {\n> > > -                       std::cerr << \"Can't queue request\" << std::endl;\n> > > +                       EPR(\"Can't queue request\\n\");\n> > >                         camera_->stop();\n> > >                         if (sink_)\n> > >                                 sink_->stop();\n> > > @@ -306,13 +296,11 @@ int CameraSession::startCapture()\n> > >         }\n> > >\n> > >         if (captureLimit_)\n> > > -               std::cout << \"cam\" << cameraIndex_\n> > > -                         << \": Capture \" << captureLimit_ << \" frames\"\n> > > -                         << std::endl;\n> > > +               PR(\"cam{}: Capture {} frames\\n\", cameraIndex_,\n> > > +                  captureLimit_);\n> > >         else\n> > > -               std::cout << \"cam\" << cameraIndex_\n> > > -                         << \": Capture until user interrupts by SIGINT\"\n> > > -                         << std::endl;\n> > > +               PR(\"cam{}: Capture until user interrupts by SIGINT\\n\",\n> > > +                  cameraIndex_);\n> > >\n> > >         return 0;\n> > >  }\n> > > @@ -364,23 +352,23 @@ void CameraSession::processRequest(Request *request)\n> > >\n> > >         bool requeue = true;\n> > >\n> > > -       std::stringstream info;\n> > > -       info << ts / 1000000000 << \".\"\n> > > -            << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000\n> > > -            << \" (\" << std::fixed << std::setprecision(2) << fps << \"\n> > > fps)\";\n> > > +       auto sbuf = fmt::memory_buffer();\n> > > +       fmt::format_to(std::back_inserter(sbuf), \"{}.{:06} ({:.2f} fps)\",\n> > > +                      ts / 1000000000,\n> > > +                      ts / 1000 % 1000000,\n> > > +                      fps);\n> > >\n> > >         for (const auto &[stream, buffer] : buffers) {\n> > >                 const FrameMetadata &metadata = buffer->metadata();\n> > >\n> > > -               info << \" \" << streamNames_[stream]\n> > > -                    << \" seq: \" << std::setw(6) << std::setfill('0') <<\n> > > metadata.sequence\n> > > -                    << \" bytesused: \";\n> > > +               fmt::format_to(std::back_inserter(sbuf), \" {} seq: {:06}\n> > > bytesused: \",\n> > > +                              streamNames_[stream], metadata.sequence);\n> > >\n> > >                 unsigned int nplane = 0;\n> > >                 for (const FrameMetadata::Plane &plane :\n> > > metadata.planes()) {\n> > > -                       info << plane.bytesused;\n> > > +                       fmt::format_to(std::back_inserter(sbuf), \"{}\",\n> > > plane.bytesused);\n> > >                         if (++nplane < metadata.planes().size())\n> > > -                               info << \"/\";\n> > > +                               fmt::format_to(std::back_inserter(sbuf),\n> > > \"/\");\n> > >                 }\n> > >         }\n> > >\n> > > @@ -389,14 +377,13 @@ void CameraSession::processRequest(Request *request)\n> > >                         requeue = false;\n> > >         }\n> > >\n> > > -       std::cout << info.str() << std::endl;\n> > > +       PR(\"{}\\n\", fmt::to_string(sbuf));\n> > >\n> > >         if (printMetadata_) {\n> > >                 const ControlList &requestMetadata = request->metadata();\n> > >                 for (const auto &[key, value] : requestMetadata) {\n> > >                         const ControlId *id = controls::controls.at(key);\n> > > -                       std::cout << \"\\t\" << id->name() << \" = \"\n> > > -                                 << value.toString() << std::endl;\n> > > +                       PR(\"\\t{} = {}\\n\", id->name(), value.toString());\n> > >                 }\n> > >         }\n> > >\n> > > diff --git a/src/cam/drm.cpp b/src/cam/drm.cpp\n> > > index 46e34eb5..84919ab3 100644\n> > > --- a/src/cam/drm.cpp\n> > > +++ b/src/cam/drm.cpp\n> > > @@ -10,12 +10,12 @@\n> > >  #include <algorithm>\n> > >  #include <errno.h>\n> > >  #include <fcntl.h>\n> > > -#include <iostream>\n> > >  #include <set>\n> > >  #include <string.h>\n> > >  #include <sys/ioctl.h>\n> > >  #include <sys/stat.h>\n> > >  #include <sys/types.h>\n> > > +#include <fmt/core.h>\n> > >\n> > >  #include <libcamera/framebuffer.h>\n> > >  #include <libcamera/geometry.h>\n> > > @@ -25,6 +25,9 @@\n> > >\n> > >  #include \"event_loop.h\"\n> > >\n> > > +#define PR(...) fmt::print(__VA_ARGS__)\n> > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > > +\n> > >  namespace DRM {\n> > >\n> > >  Object::Object(Device *dev, uint32_t id, Type type)\n> > > @@ -178,9 +181,7 @@ Connector::Connector(Device *dev, const\n> > > drmModeConnector *connector)\n> > >  {\n> > >         auto typeName = connectorTypeNames.find(connector->connector_type);\n> > >         if (typeName == connectorTypeNames.end()) {\n> > > -               std::cerr\n> > > -                       << \"Invalid connector type \"\n> > > -                       << connector->connector_type << std::endl;\n> > > +               EPR(\"Invalid connector type {}}n\",\n> > > connector->connector_type);\n> > >                 typeName =\n> > > connectorTypeNames.find(DRM_MODE_CONNECTOR_Unknown);\n> > >         }\n> > >\n> > > @@ -213,9 +214,7 @@ Connector::Connector(Device *dev, const\n> > > drmModeConnector *connector)\n> > >                                                     return e.id() ==\n> > > encoderId;\n> > >                                             });\n> > >                 if (encoder == encoders.end()) {\n> > > -                       std::cerr\n> > > -                               << \"Encoder \" << encoderId << \" not found\"\n> > > -                               << std::endl;\n> > > +                       EPR(\"Encoder {} not found\\n\", encoderId);\n> > >                         continue;\n> > >                 }\n> > >\n> > > @@ -296,9 +295,7 @@ FrameBuffer::~FrameBuffer()\n> > >\n> > >                 if (ret == -1) {\n> > >                         ret = -errno;\n> > > -                       std::cerr\n> > > -                               << \"Failed to close GEM object: \"\n> > > -                               << strerror(-ret) << std::endl;\n> > > +                       EPR(\"Failed to close GEM object: {}\\n\",\n> > > strerror(-ret));\n> > >                 }\n> > >         }\n> > >\n> > > @@ -408,9 +405,8 @@ int Device::init()\n> > >         fd_ = open(name, O_RDWR | O_CLOEXEC);\n> > >         if (fd_ < 0) {\n> > >                 ret = -errno;\n> > > -               std::cerr\n> > > -                       << \"Failed to open DRM/KMS device \" << name << \": \"\n> > > -                       << strerror(-ret) << std::endl;\n> > > +               EPR(\"Failed to open DRM/KMS device {}: {}\\n\", name,\n> > > +                   strerror(-ret));\n> > >                 return ret;\n> > >         }\n> > >\n> > > @@ -421,9 +417,7 @@ int Device::init()\n> > >         ret = drmSetClientCap(fd_, DRM_CLIENT_CAP_ATOMIC, 1);\n> > >         if (ret < 0) {\n> > >                 ret = -errno;\n> > > -               std::cerr\n> > > -                       << \"Failed to enable atomic capability: \"\n> > > -                       << strerror(-ret) << std::endl;\n> > > +               EPR(\"Failed to enable atomic capability: {}\\n\",\n> > > strerror(-ret));\n> > >                 return ret;\n> > >         }\n> > >\n> > > @@ -448,9 +442,7 @@ int Device::getResources()\n> > >         };\n> > >         if (!resources) {\n> > >                 ret = -errno;\n> > > -               std::cerr\n> > > -                       << \"Failed to get DRM/KMS resources: \"\n> > > -                       << strerror(-ret) << std::endl;\n> > > +               EPR(\"Failed to get DRM/KMS resources: {}\\n\",\n> > > strerror(-ret));\n> > >                 return ret;\n> > >         }\n> > >\n> > > @@ -458,9 +450,7 @@ int Device::getResources()\n> > >                 drmModeCrtc *crtc = drmModeGetCrtc(fd_,\n> > > resources->crtcs[i]);\n> > >                 if (!crtc) {\n> > >                         ret = -errno;\n> > > -                       std::cerr\n> > > -                               << \"Failed to get CRTC: \" << strerror(-ret)\n> > > -                               << std::endl;\n> > > +                       EPR(\"Failed to get CRTC: {}\\n\", strerror(-ret));\n> > >                         return ret;\n> > >                 }\n> > >\n> > > @@ -476,9 +466,7 @@ int Device::getResources()\n> > >                         drmModeGetEncoder(fd_, resources->encoders[i]);\n> > >                 if (!encoder) {\n> > >                         ret = -errno;\n> > > -                       std::cerr\n> > > -                               << \"Failed to get encoder: \" <<\n> > > strerror(-ret)\n> > > -                               << std::endl;\n> > > +                       EPR(\"Failed to get encoder: {}\\n\", strerror(-ret));\n> > >                         return ret;\n> > >                 }\n> > >\n> > > @@ -494,9 +482,7 @@ int Device::getResources()\n> > >                         drmModeGetConnector(fd_, resources->connectors[i]);\n> > >                 if (!connector) {\n> > >                         ret = -errno;\n> > > -                       std::cerr\n> > > -                               << \"Failed to get connector: \" <<\n> > > strerror(-ret)\n> > > -                               << std::endl;\n> > > +                       EPR(\"Failed to get connector: {}\\n\",\n> > > strerror(-ret));\n> > >                         return ret;\n> > >                 }\n> > >\n> > > @@ -513,9 +499,7 @@ int Device::getResources()\n> > >         };\n> > >         if (!planes) {\n> > >                 ret = -errno;\n> > > -               std::cerr\n> > > -                       << \"Failed to get DRM/KMS planes: \"\n> > > -                       << strerror(-ret) << std::endl;\n> > > +               EPR(\"Failed to get DRM/KMS planes: {}\\n\", strerror(-ret));\n> > >                 return ret;\n> > >         }\n> > >\n> > > @@ -524,9 +508,7 @@ int Device::getResources()\n> > >                         drmModeGetPlane(fd_, planes->planes[i]);\n> > >                 if (!plane) {\n> > >                         ret = -errno;\n> > > -                       std::cerr\n> > > -                               << \"Failed to get plane: \" <<\n> > > strerror(-ret)\n> > > -                               << std::endl;\n> > > +                       EPR(\"Failed to get plane: {}\\n\", strerror(-ret));\n> > >                         return ret;\n> > >                 }\n> > >\n> > > @@ -556,9 +538,7 @@ int Device::getResources()\n> > >                 drmModePropertyRes *property = drmModeGetProperty(fd_, id);\n> > >                 if (!property) {\n> > >                         ret = -errno;\n> > > -                       std::cerr\n> > > -                               << \"Failed to get property: \" <<\n> > > strerror(-ret)\n> > > -                               << std::endl;\n> > > +                       EPR(\"Failed to get property: {}\\n\",\n> > > strerror(-ret));\n> > >                         continue;\n> > >                 }\n> > >\n> > > @@ -573,9 +553,8 @@ int Device::getResources()\n> > >         for (auto &object : objects_) {\n> > >                 ret = object.second->setup();\n> > >                 if (ret < 0) {\n> > > -                       std::cerr\n> > > -                               << \"Failed to setup object \" <<\n> > > object.second->id()\n> > > -                               << \": \" << strerror(-ret) << std::endl;\n> > > +                       EPR(\"Failed to setup object {}: {}\\n\",\n> > > +                           object.second->id(), strerror(-ret));\n> > >                         return ret;\n> > >                 }\n> > >         }\n> > > @@ -616,9 +595,8 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(\n> > >                         ret = drmPrimeFDToHandle(fd_, plane.fd.get(),\n> > > &handle);\n> > >                         if (ret < 0) {\n> > >                                 ret = -errno;\n> > > -                               std::cerr\n> > > -                                       << \"Unable to import framebuffer\n> > > dmabuf: \"\n> > > -                                       << strerror(-ret) << std::endl;\n> > > +                               EPR(\"Unable to import framebuffer dmabuf:\n> > > {}\\n\",\n> > > +                                   strerror(-ret));\n> > >                                 return nullptr;\n> > >                         }\n> > >\n> > > @@ -636,9 +614,7 @@ std::unique_ptr<FrameBuffer> Device::createFrameBuffer(\n> > >                             strides.data(), offsets, &fb->id_, 0);\n> > >         if (ret < 0) {\n> > >                 ret = -errno;\n> > > -               std::cerr\n> > > -                       << \"Failed to add framebuffer: \"\n> > > -                       << strerror(-ret) << std::endl;\n> > > +               EPR(\"Failed to add framebuffer: {}\\n\", strerror(-ret));\n> > >                 return nullptr;\n> > >         }\n> > >\n> > > diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp\n> > > index e25784c0..87aaf59a 100644\n> > > --- a/src/cam/event_loop.cpp\n> > > +++ b/src/cam/event_loop.cpp\n> > > @@ -10,7 +10,10 @@\n> > >  #include <assert.h>\n> > >  #include <event2/event.h>\n> > >  #include <event2/thread.h>\n> > > -#include <iostream>\n> > > +#include <fmt/core.h>\n> > > +\n> > > +#define PR(...) fmt::print(__VA_ARGS__)\n> > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > >\n> > >  EventLoop *EventLoop::instance_ = nullptr;\n> > >\n> > > @@ -71,13 +74,13 @@ void EventLoop::addEvent(int fd, EventType type,\n> > >         event->event_ = event_new(base_, fd, events,\n> > > &EventLoop::Event::dispatch,\n> > >                                   event.get());\n> > >         if (!event->event_) {\n> > > -               std::cerr << \"Failed to create event for fd \" << fd <<\n> > > std::endl;\n> > > +               EPR(\"Failed to create event for fd {}\\n\", fd);\n> > >                 return;\n> > >         }\n> > >\n> > >         int ret = event_add(event->event_, nullptr);\n> > >         if (ret < 0) {\n> > > -               std::cerr << \"Failed to add event for fd \" << fd <<\n> > > std::endl;\n> > > +               EPR(\"Failed to add event for fd {}\\n\", fd);\n> > >                 return;\n> > >         }\n> > >\n> > > diff --git a/src/cam/file_sink.cpp b/src/cam/file_sink.cpp\n> > > index 45213d4a..86e2118c 100644\n> > > --- a/src/cam/file_sink.cpp\n> > > +++ b/src/cam/file_sink.cpp\n> > > @@ -7,11 +7,12 @@\n> > >\n> > >  #include <assert.h>\n> > >  #include <fcntl.h>\n> > > -#include <iomanip>\n> > > -#include <iostream>\n> > > -#include <sstream>\n> > >  #include <string.h>\n> > >  #include <unistd.h>\n> > > +#include <fmt/core.h>\n> > > +\n> > > +#define PR(...) fmt::print(__VA_ARGS__)\n> > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > >\n> > >  #include <libcamera/camera.h>\n> > >\n> > > @@ -70,10 +71,10 @@ void FileSink::writeBuffer(const Stream *stream,\n> > > FrameBuffer *buffer)\n> > >\n> > >         pos = filename.find_first_of('#');\n> > >         if (pos != std::string::npos) {\n> > > -               std::stringstream ss;\n> > > -               ss << streamNames_[stream] << \"-\" << std::setw(6)\n> > > -                  << std::setfill('0') << buffer->metadata().sequence;\n> > > -               filename.replace(pos, 1, ss.str());\n> > > +               auto s = fmt::format(\"{}-{:06}\",\n> > > +                                    streamNames_[stream],\n> > > +                                    buffer->metadata().sequence);\n> > > +               filename.replace(pos, 1, s);\n> > >         }\n> > >\n> > >         fd = open(filename.c_str(), O_CREAT | O_WRONLY |\n> > > @@ -81,8 +82,7 @@ void FileSink::writeBuffer(const Stream *stream,\n> > > FrameBuffer *buffer)\n> > >                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |\n> > > S_IWOTH);\n> > >         if (fd == -1) {\n> > >                 ret = -errno;\n> > > -               std::cerr << \"failed to open file \" << filename << \": \"\n> > > -                         << strerror(-ret) << std::endl;\n> > > +               EPR(\"failed to open file {}: {}\\n\", filename,\n> > > strerror(-ret));\n> > >                 return;\n> > >         }\n> > >\n> > > @@ -95,20 +95,17 @@ void FileSink::writeBuffer(const Stream *stream,\n> > > FrameBuffer *buffer)\n> > >                 unsigned int length = std::min<unsigned\n> > > int>(meta.bytesused, data.size());\n> > >\n> > >                 if (meta.bytesused > data.size())\n> > > -                       std::cerr << \"payload size \" << meta.bytesused\n> > > -                                 << \" larger than plane size \" <<\n> > > data.size()\n> > > -                                 << std::endl;\n> > > +                       EPR(\"payload size {} larger than plane size {}\\n\",\n> > > +                           meta.bytesused, data.size());\n> > >\n> > >                 ret = ::write(fd, data.data(), length);\n> > >                 if (ret < 0) {\n> > >                         ret = -errno;\n> > > -                       std::cerr << \"write error: \" << strerror(-ret)\n> > > -                                 << std::endl;\n> > > +                       EPR(\"write error: {}\\n\", strerror(-ret));\n> > >                         break;\n> > >                 } else if (ret != (int)length) {\n> > > -                       std::cerr << \"write error: only \" << ret\n> > > -                                 << \" bytes written instead of \"\n> > > -                                 << length << std::endl;\n> > > +                       EPR(\"write error: only {} bytes written instead of\n> > > {}\\n\",\n> > > +                           ret, length);\n> > >                         break;\n> > >                 }\n> > >         }\n> > > diff --git a/src/cam/image.cpp b/src/cam/image.cpp\n> > > index fe2cc6da..73bcf915 100644\n> > > --- a/src/cam/image.cpp\n> > > +++ b/src/cam/image.cpp\n> > > @@ -9,11 +9,14 @@\n> > >\n> > >  #include <assert.h>\n> > >  #include <errno.h>\n> > > -#include <iostream>\n> > >  #include <map>\n> > >  #include <string.h>\n> > >  #include <sys/mman.h>\n> > >  #include <unistd.h>\n> > > +#include <fmt/core.h>\n> > > +\n> > > +#define PR(...) fmt::print(__VA_ARGS__)\n> > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > >\n> > >  using namespace libcamera;\n> > >\n> > > @@ -49,10 +52,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const\n> > > FrameBuffer *buffer, MapMode\n> > >\n> > >                 if (plane.offset > length ||\n> > >                     plane.offset + plane.length > length) {\n> > > -                       std::cerr << \"plane is out of buffer: buffer\n> > > length=\"\n> > > -                                 << length << \", plane offset=\" <<\n> > > plane.offset\n> > > -                                 << \", plane length=\" << plane.length\n> > > -                                 << std::endl;\n> > > +                       EPR(\"plane is out of buffer: buffer length={},\n> > > plane offset={}, plane length={}\\n\",\n> > > +                           length, plane.offset, plane.length);\n> > >                         return nullptr;\n> > >                 }\n> > >                 size_t &mapLength = mappedBuffers[fd].mapLength;\n> > > @@ -68,8 +69,8 @@ std::unique_ptr<Image> Image::fromFrameBuffer(const\n> > > FrameBuffer *buffer, MapMode\n> > >                                              MAP_SHARED, fd, 0);\n> > >                         if (address == MAP_FAILED) {\n> > >                                 int error = -errno;\n> > > -                               std::cerr << \"Failed to mmap plane: \"\n> > > -                                         << strerror(-error) << std::endl;\n> > > +                               EPR(\"Failed to mmap plane: {}\\n\",\n> > > +                                   strerror(-error));\n> > >                                 return nullptr;\n> > >                         }\n> > >\n> > > diff --git a/src/cam/kms_sink.cpp b/src/cam/kms_sink.cpp\n> > > index 7add81a6..823b75e4 100644\n> > > --- a/src/cam/kms_sink.cpp\n> > > +++ b/src/cam/kms_sink.cpp\n> > > @@ -10,10 +10,13 @@\n> > >  #include <array>\n> > >  #include <algorithm>\n> > >  #include <assert.h>\n> > > -#include <iostream>\n> > >  #include <memory>\n> > >  #include <stdint.h>\n> > >  #include <string.h>\n> > > +#include <fmt/core.h>\n> > > +\n> > > +#define PR(...) fmt::print(__VA_ARGS__)\n> > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > >\n> > >  #include <libcamera/camera.h>\n> > >  #include <libcamera/formats.h>\n> > > @@ -54,11 +57,9 @@ KMSSink::KMSSink(const std::string &connectorName)\n> > >\n> > >         if (!connector_) {\n> > >                 if (!connectorName.empty())\n> > > -                       std::cerr\n> > > -                               << \"Connector \" << connectorName << \" not\n> > > found\"\n> > > -                               << std::endl;\n> > > +                       EPR(\"Connector {} not found\\n\", connectorName);\n> > >                 else\n> > > -                       std::cerr << \"No connected connector found\" <<\n> > > std::endl;\n> > > +                       EPR(\"No connected connector found\\n\");\n> > >                 return;\n> > >         }\n> > >\n> > > @@ -119,7 +120,7 @@ int KMSSink::configure(const\n> > > libcamera::CameraConfiguration &config)\n> > >                                                       mode.vdisplay ==\n> > > cfg.size.height;\n> > >                                        });\n> > >         if (iter == modes.end()) {\n> > > -               std::cerr << \"No mode matching \" << cfg.size << std::endl;\n> > > +               EPR(\"No mode matching {}\\n\", cfg.size);\n> > >                 return -EINVAL;\n> > >         }\n> > >\n> > > @@ -192,17 +193,12 @@ int KMSSink::configurePipeline(const\n> > > libcamera::PixelFormat &format)\n> > >  {\n> > >         const int ret = selectPipeline(format);\n> > >         if (ret) {\n> > > -               std::cerr\n> > > -                       << \"Unable to find display pipeline for format \"\n> > > -                       << format << std::endl;\n> > > -\n> > > +               EPR(\"Unable to find display pipeline for format {}\\n\",\n> > > format);\n> > >                 return ret;\n> > >         }\n> > >\n> > > -       std::cout\n> > > -               << \"Using KMS plane \" << plane_->id() << \", CRTC \" <<\n> > > crtc_->id()\n> > > -               << \", connector \" << connector_->name()\n> > > -               << \" (\" << connector_->id() << \")\" << std::endl;\n> > > +       PR(\"Using KMS plane {}, CRTC {}, connector {} ({})\\n\",\n> > > +          plane_->id(), crtc_->id(), connector_->name(),\n> > > connector_->id());\n> > >\n> > >         return 0;\n> > >  }\n> > > @@ -228,9 +224,8 @@ int KMSSink::start()\n> > >\n> > >         ret = request->commit(DRM::AtomicRequest::FlagAllowModeset);\n> > >         if (ret < 0) {\n> > > -               std::cerr\n> > > -                       << \"Failed to disable CRTCs and planes: \"\n> > > -                       << strerror(-ret) << std::endl;\n> > > +               EPR(\"Failed to disable CRTCs and planes: {}\\n\",\n> > > +                   strerror(-ret));\n> > >                 return ret;\n> > >         }\n> > >\n> > > @@ -250,9 +245,7 @@ int KMSSink::stop()\n> > >\n> > >         int ret = request.commit(DRM::AtomicRequest::FlagAllowModeset);\n> > >         if (ret < 0) {\n> > > -               std::cerr\n> > > -                       << \"Failed to stop display pipeline: \"\n> > > -                       << strerror(-ret) << std::endl;\n> > > +               EPR(\"Failed to stop display pipeline: {}\\n\",\n> > > strerror(-ret));\n> > >                 return ret;\n> > >         }\n> > >\n> > > @@ -312,9 +305,8 @@ bool KMSSink::processRequest(libcamera::Request\n> > > *camRequest)\n> > >         if (!queued_) {\n> > >                 int ret = drmRequest->commit(flags);\n> > >                 if (ret < 0) {\n> > > -                       std::cerr\n> > > -                               << \"Failed to commit atomic request: \"\n> > > -                               << strerror(-ret) << std::endl;\n> > > +                       EPR(\"Failed to commit atomic request: {}\\n\",\n> > > +                           strerror(-ret));\n> > >                         /* \\todo Implement error handling */\n> > >                 }\n> > >\n> > > diff --git a/src/cam/main.cpp b/src/cam/main.cpp\n> > > index c7f664b9..03615dc9 100644\n> > > --- a/src/cam/main.cpp\n> > > +++ b/src/cam/main.cpp\n> > > @@ -6,10 +6,9 @@\n> > >   */\n> > >\n> > >  #include <atomic>\n> > > -#include <iomanip>\n> > > -#include <iostream>\n> > >  #include <signal.h>\n> > >  #include <string.h>\n> > > +#include <fmt/core.h>\n> > >\n> > >  #include <libcamera/libcamera.h>\n> > >  #include <libcamera/property_ids.h>\n> > > @@ -78,8 +77,7 @@ int CamApp::init(int argc, char **argv)\n> > >\n> > >         ret = cm_->start();\n> > >         if (ret) {\n> > > -               std::cout << \"Failed to start camera manager: \"\n> > > -                         << strerror(-ret) << std::endl;\n> > > +               fmt::print(\"Failed to start camera manager: {}\\n\", -ret);\n> > >                 return ret;\n> > >         }\n> > >\n> > > @@ -173,12 +171,12 @@ int CamApp::parseOptions(int argc, char *argv[])\n> > >\n> > >  void CamApp::cameraAdded(std::shared_ptr<Camera> cam)\n> > >  {\n> > > -       std::cout << \"Camera Added: \" << cam->id() << std::endl;\n> > > +       fmt::print(\"Camera Added: {}\\n\", cam->id());\n> > >  }\n> > >\n> > >  void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)\n> > >  {\n> > > -       std::cout << \"Camera Removed: \" << cam->id() << std::endl;\n> > > +       fmt::print(\"Camera Removed: {}\\n\", cam->id());\n> > >  }\n> > >\n> > >  void CamApp::captureDone()\n> > > @@ -193,11 +191,11 @@ int CamApp::run()\n> > >\n> > >         /* 1. List all cameras. */\n> > >         if (options_.isSet(OptList)) {\n> > > -               std::cout << \"Available cameras:\" << std::endl;\n> > > +               fmt::print(\"Available cameras:\\n\");\n> > >\n> > >                 unsigned int index = 1;\n> > >                 for (const std::shared_ptr<Camera> &cam : cm_->cameras()) {\n> > > -                       std::cout << index << \": \" <<\n> > > cameraName(cam.get()) << std::endl;\n> > > +                       fmt::print(\"{}: {}\\n\", cameraName(cam.get()),\n> > > index);\n> > >                         index++;\n> > >                 }\n> > >         }\n> > > @@ -215,12 +213,12 @@ int CamApp::run()\n> > >                                                                 index,\n> > >\n> > > camera.children());\n> > >                         if (!session->isValid()) {\n> > > -                               std::cout << \"Failed to create camera\n> > > session\" << std::endl;\n> > > +                               fmt::print(\"Failed to create camera\n> > > session\\n\");\n> > >                                 return -EINVAL;\n> > >                         }\n> > >\n> > > -                       std::cout << \"Using camera \" <<\n> > > session->camera()->id()\n> > > -                                 << \" as cam\" << index << std::endl;\n> > > +                       fmt::print(\"Using camera{} as cam{}\\n\",\n> > > +                                  session->camera()->id(), index);\n> > >\n> > >                         session->captureDone.connect(this,\n> > > &CamApp::captureDone);\n> > >\n> > > @@ -250,7 +248,7 @@ int CamApp::run()\n> > >\n> > >                 ret = session->start();\n> > >                 if (ret) {\n> > > -                       std::cout << \"Failed to start camera session\" <<\n> > > std::endl;\n> > > +                       fmt::print(\"Failed to start camera session\\n\");\n> > >                         return ret;\n> > >                 }\n> > >\n> > > @@ -259,8 +257,8 @@ int CamApp::run()\n> > >\n> > >         /* 5. Enable hotplug monitoring. */\n> > >         if (options_.isSet(OptMonitor)) {\n> > > -               std::cout << \"Monitoring new hotplug and unplug events\" <<\n> > > std::endl;\n> > > -               std::cout << \"Press Ctrl-C to interrupt\" << std::endl;\n> > > +               fmt::print(\"Monitoring new hotplug and unplug events\\n\");\n> > > +               fmt::print(\"Press Ctrl-C to interrupt\\n\");\n> > >\n> > >                 cm_->cameraAdded.connect(this, &CamApp::cameraAdded);\n> > >                 cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);\n> > > @@ -323,7 +321,7 @@ std::string CamApp::cameraName(const Camera *camera)\n> > >\n> > >  void signalHandler([[maybe_unused]] int signal)\n> > >  {\n> > > -       std::cout << \"Exiting\" << std::endl;\n> > > +       fmt::print(\"Exiting\");\n> > >         CamApp::instance()->quit();\n> > >  }\n> > >\n> > > diff --git a/src/cam/meson.build b/src/cam/meson.build\n> > > index 5bab8c9e..2b47383d 100644\n> > > --- a/src/cam/meson.build\n> > > +++ b/src/cam/meson.build\n> > > @@ -7,6 +7,8 @@ if not libevent.found()\n> > >      subdir_done()\n> > >  endif\n> > >\n> > > +libfmt_dep = dependency('fmt')\n> > > +\n> > >  cam_enabled = true\n> > >\n> > >  cam_sources = files([\n> > > @@ -25,7 +27,7 @@ cam_cpp_args = []\n> > >  libdrm = dependency('libdrm', required : false)\n> > >\n> > >  if libdrm.found()\n> > > -    cam_cpp_args += [ '-DHAVE_KMS' ]\n> > > +    cam_cpp_args += [ '-DHAVE_KMS', ]\n> > >      cam_sources += files([\n> > >          'drm.cpp',\n> > >          'kms_sink.cpp'\n> > > @@ -38,6 +40,7 @@ cam  = executable('cam', cam_sources,\n> > >                        libcamera_public,\n> > >                        libdrm,\n> > >                        libevent,\n> > > +                      libfmt_dep,\n> > >                    ],\n> > >                    cpp_args : cam_cpp_args,\n> > >                    install : true)\n> > > diff --git a/src/cam/options.cpp b/src/cam/options.cpp\n> > > index 4f7e8691..c9979385 100644\n> > > --- a/src/cam/options.cpp\n> > > +++ b/src/cam/options.cpp\n> > > @@ -7,9 +7,11 @@\n> > >\n> > >  #include <assert.h>\n> > >  #include <getopt.h>\n> > > -#include <iomanip>\n> > > -#include <iostream>\n> > >  #include <string.h>\n> > > +#include <fmt/core.h>\n> > > +\n> > > +#define PR(...) fmt::print(__VA_ARGS__)\n> > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > >\n> > >  #include \"options.h\"\n> > >\n> > > @@ -390,26 +392,23 @@ KeyValueParser::Options KeyValueParser::parse(const\n> > > char *arguments)\n> > >                         continue;\n> > >\n> > >                 if (optionsMap_.find(key) == optionsMap_.end()) {\n> > > -                       std::cerr << \"Invalid option \" << key << std::endl;\n> > > +                       EPR(\"Invalid option {}\\n\", key);\n> > >                         return options;\n> > >                 }\n> > >\n> > >                 OptionArgument arg = optionsMap_[key].argument;\n> > >                 if (value.empty() && arg == ArgumentRequired) {\n> > > -                       std::cerr << \"Option \" << key << \" requires an\n> > > argument\"\n> > > -                                 << std::endl;\n> > > +                       EPR(\"Option {} requires an argument\\n\", key);\n> > >                         return options;\n> > >                 } else if (!value.empty() && arg == ArgumentNone) {\n> > > -                       std::cerr << \"Option \" << key << \" takes no\n> > > argument\"\n> > > -                                 << std::endl;\n> > > +                       EPR(\"Option {} takes no argument\\n\", key);\n> > >                         return options;\n> > >                 }\n> > >\n> > >                 const Option &option = optionsMap_[key];\n> > >                 if (!options.parseValue(key, option, value.c_str())) {\n> > > -                       std::cerr << \"Failed to parse '\" << value << \"' as\n> > > \"\n> > > -                                 << option.typeName() << \" for option \"\n> > > << key\n> > > -                                 << std::endl;\n> > > +                       EPR(\"Failed to parse '{}' as {} for option {}\\n\",\n> > > +                           value, option.typeName(), key);\n> > >                         return options;\n> > >                 }\n> > >         }\n> > > @@ -453,16 +452,16 @@ void KeyValueParser::usage(int indent)\n> > >                                 argument += \"]\";\n> > >                 }\n> > >\n> > > -               std::cerr << std::setw(indent) << argument;\n> > > +               EPR(\"{:{}}\", argument, indent);\n> > >\n> > >                 for (const char *help = option.help, *end = help; end;) {\n> > >                         end = strchr(help, '\\n');\n> > >                         if (end) {\n> > > -                               std::cerr << std::string(help, end - help\n> > > + 1);\n> > > -                               std::cerr << std::setw(indent) << \" \";\n> > > +                               EPR(std::string(help, end - help + 1));\n> > > +                               EPR(\"{:{}}\", \"\", indent);\n> > >                                 help = end + 1;\n> > >                         } else {\n> > > -                               std::cerr << help << std::endl;\n> > > +                               EPR(\"{}\\n\", help);\n> > >                         }\n> > >                 }\n> > >         }\n> > > @@ -929,10 +928,10 @@ OptionsParser::Options OptionsParser::parse(int\n> > > argc, char **argv)\n> > >\n> > >                 if (c == '?' || c == ':') {\n> > >                         if (c == '?')\n> > > -                               std::cerr << \"Invalid option \";\n> > > +                               EPR(\"Invalid option \");\n> > >                         else\n> > > -                               std::cerr << \"Missing argument for option\n> > > \";\n> > > -                       std::cerr << argv[optind - 1] << std::endl;\n> > > +                               EPR(\"Missing argument for option \");\n> > > +                       EPR(\"{}\\n\", argv[optind - 1]);\n> > >\n> > >                         usage();\n> > >                         return options;\n> > > @@ -946,8 +945,7 @@ OptionsParser::Options OptionsParser::parse(int argc,\n> > > char **argv)\n> > >         }\n> > >\n> > >         if (optind < argc) {\n> > > -               std::cerr << \"Invalid non-option argument '\" <<\n> > > argv[optind]\n> > > -                         << \"'\" << std::endl;\n> > > +               EPR(\"Invalid non-option argument '{}'\\n\", argv[optind]);\n> > >                 usage();\n> > >                 return options;\n> > >         }\n> > > @@ -992,14 +990,9 @@ void OptionsParser::usage()\n> > >\n> > >         indent = (indent + 7) / 8 * 8;\n> > >\n> > > -       std::cerr << \"Options:\" << std::endl;\n> > > -\n> > > -       std::ios_base::fmtflags f(std::cerr.flags());\n> > > -       std::cerr << std::left;\n> > > +       EPR(\"Options:\\n\");\n> > >\n> > >         usageOptions(options_, indent);\n> > > -\n> > > -       std::cerr.flags(f);\n> > >  }\n> > >\n> > >  void OptionsParser::usageOptions(const std::list<Option> &options,\n> > > @@ -1036,16 +1029,16 @@ void OptionsParser::usageOptions(const\n> > > std::list<Option> &options,\n> > >                 if (option.isArray)\n> > >                         argument += \" ...\";\n> > >\n> > > -               std::cerr << std::setw(indent) << argument;\n> > > +               EPR(\"{:{}}\", argument, indent);\n> > >\n> > > -               for (const char *help = option.help, *end = help; end; ) {\n> > > +               for (const char *help = option.help, *end = help; end;) {\n> > >                         end = strchr(help, '\\n');\n> > >                         if (end) {\n> > > -                               std::cerr << std::string(help, end - help\n> > > + 1);\n> > > -                               std::cerr << std::setw(indent) << \" \";\n> > > +                               EPR(std::string(help, end - help + 1));\n> > > +                               EPR(\"{:{}}\", \"\", indent);\n> > >                                 help = end + 1;\n> > >                         } else {\n> > > -                               std::cerr << help << std::endl;\n> > > +                               EPR(\"{}\\n\", help);\n> > >                         }\n> > >                 }\n> > >\n> > > @@ -1060,8 +1053,8 @@ void OptionsParser::usageOptions(const\n> > > std::list<Option> &options,\n> > >                 return;\n> > >\n> > >         for (const Option *option : parentOptions) {\n> > > -               std::cerr << std::endl << \"Options valid in the context of\n> > > \"\n> > > -                         << option->optionName() << \":\" << std::endl;\n> > > +               EPR(\"\\nOptions valid in the context of {}:\\n\",\n> > > +                   option->optionName());\n> > >                 usageOptions(option->children, indent);\n> > >         }\n> > >  }\n> > > @@ -1125,15 +1118,14 @@ bool OptionsParser::parseValue(const Option\n> > > &option, const char *arg,\n> > >\n> > >         std::tie(options, error) = childOption(option.parent, options);\n> > >         if (error) {\n> > > -               std::cerr << \"Option \" << option.optionName() << \"\n> > > requires a \"\n> > > -                         << error->optionName() << \" context\" <<\n> > > std::endl;\n> > > +               EPR(\"Option {} requires a {} context\\n\",\n> > > +                   option.optionName(), error->optionName());\n> > >                 return false;\n> > >         }\n> > >\n> > >         if (!options->parseValue(option.opt, option, arg)) {\n> > > -               std::cerr << \"Can't parse \" << option.typeName()\n> > > -                         << \" argument for option \" << option.optionName()\n> > > -                         << std::endl;\n> > > +               EPR(\"Can't parse {} argument for option {}\\n\",\n> > > +                   option.typeName(), option.optionName());\n> > >                 return false;\n> > >         }\n> > >\n> > > diff --git a/src/cam/stream_options.cpp b/src/cam/stream_options.cpp\n> > > index 150bd27c..666862eb 100644\n> > > --- a/src/cam/stream_options.cpp\n> > > +++ b/src/cam/stream_options.cpp\n> > > @@ -6,7 +6,10 @@\n> > >   */\n> > >  #include \"stream_options.h\"\n> > >\n> > > -#include <iostream>\n> > > +#include <fmt/core.h>\n> > > +\n> > > +#define PR(...) fmt::print(__VA_ARGS__)\n> > > +#define EPR(...) fmt::print(stderr, __VA_ARGS__)\n> > >\n> > >  using namespace libcamera;\n> > >\n> > > @@ -30,8 +33,7 @@ KeyValueParser::Options\n> > > StreamKeyValueParser::parse(const char *arguments)\n> > >\n> > >         if (options.valid() && options.isSet(\"role\") &&\n> > >             !parseRole(&role, options)) {\n> > > -               std::cerr << \"Unknown stream role \"\n> > > -                         << options[\"role\"].toString() << std::endl;\n> > > +               EPR(\"Unknown stream role {}\\n\",\n> > > options[\"role\"].toString());\n> > >                 options.invalidate();\n> > >         }\n> > >\n> > > @@ -64,7 +66,7 @@ int\n> > > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,\n> > >                                               const OptionValue &values)\n> > >  {\n> > >         if (!config) {\n> > > -               std::cerr << \"No configuration provided\" << std::endl;\n> > > +               EPR(\"No configuration provided\\n\");\n> > >                 return -EINVAL;\n> > >         }\n> > >\n> > > @@ -75,12 +77,8 @@ int\n> > > StreamKeyValueParser::updateConfiguration(CameraConfiguration *config,\n> > >         const std::vector<OptionValue> &streamParameters =\n> > > values.toArray();\n> > >\n> > >         if (config->size() != streamParameters.size()) {\n> > > -               std::cerr\n> > > -                       << \"Number of streams in configuration \"\n> > > -                       << config->size()\n> > > -                       << \" does not match number of streams parsed \"\n> > > -                       << streamParameters.size()\n> > > -                       << std::endl;\n> > > +               EPR(\"Number of streams in configuration {} does not match\n> > > number of streams parsed {}\\n\",\n> > > +                   config->size(), streamParameters.size());\n> > >                 return -EINVAL;\n> > >         }\n> > >\n> > > --\n> > > 2.34.1\n> > >\n> > >\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 EABCFC3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 13:00:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F1E6065646;\n\tTue, 10 May 2022 15:00:00 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9FF6265643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 14:59:58 +0200 (CEST)","from mail-oo1-f69.google.com (mail-oo1-f69.google.com\n\t[209.85.161.69]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n\tus-mta-527-CX28LrGwOiWIcXT7pEnH7Q-1; Tue, 10 May 2022 08:59:56 -0400","by mail-oo1-f69.google.com with SMTP id\n\tn4-20020a4a8484000000b0035f4d798c46so6058562oog.21\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 05:59:56 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652187601;\n\tbh=Vds/q6k58JFtJ1Qxmv2XRyUmVCAKg9eyr0avD3pG1ec=;\n\th=References:In-Reply-To:Date:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=gMT/sILTxzVgKM3mP/LHCHy1svT1GcLPbilLJgeIC8/EUTr9wev3EWcxkvOrv8B7t\n\tPce9zCR2HGtOkBXHEVBjUNeAidAngD4/aauvsu34aA9quCaJu4ltTIqhZrJAanpxU5\n\tg9wn9dbMEkx+cpbt6NJjsKM11aJN3n1C+eomfjAnKfISsdnAi7K8Oa8e9gLqy/Rmxl\n\tulx/VfBOgBTX0dkcSg6AAWqaYYUAxWgrbE8ADyQO4M9uowY3+pnGGsS4wsOmNlInP9\n\tRSfSWVSow2fqw0A7EJmVZ8l72Tncw6bRY+zHtFrRbjbD+t6hTGxpWwya1hpS88PFxW\n\tg85QPqelTRXag==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1652187597;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=FbVGmJoZthxLxIhX5Pqtf9VPvPdCzm+FZ1qC+sugzkQ=;\n\tb=CYb/0sIJRQSI/1lsJNBEaZDmtxu/33RjXNmEPycQZgmH96Xo9LnqDjlp7W5/l2lnMr04bO\n\t4QG99wKvyxBHh2+Ofo1jCgEMkyXwqByFNF+3sv1SQRrErDBvB3b8ZUUfTvRNJ+d2kahDJc\n\tvx7A+wpnNK5QMOxQdIbMAmEysVtOlDM="],"Authentication-Results":["lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=redhat.com\n\theader.i=@redhat.com header.b=\"CYb/0sIJ\"; \n\tdkim-atps=neutral","relay.mimecast.com;\n\tauth=pass smtp.auth=CUSA124A263 smtp.mailfrom=ecurtin@redhat.com"],"X-MC-Unique":"CX28LrGwOiWIcXT7pEnH7Q-1","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=FbVGmJoZthxLxIhX5Pqtf9VPvPdCzm+FZ1qC+sugzkQ=;\n\tb=PzUUEI1wX/0dRwCH2qCll/fG8DKHA0OBc9NpNV/J/10jinM/qxAXGFZAVe/RR9U2gY\n\t2RZeZDEO5x2x+cOm5OGEqwBz7dN4WmL+bq1sk5z3Om37E0Xk5Y0pAgmsw6ei1mHP4iMW\n\teVHCnxgry1z9y6feMqN9QYQWeBNXgRRPPtgek2DF/RWLYaYtHBHoKUQhurBys0lJNCBL\n\t18Mfhcdl779krttS5VB0ImQ+kjuMLZ2q8w513ZOQF3U1slnuMrtSLp7V7KMm/30e1eOe\n\tI6czi3JwHEEbl/bQAVGawV7oF4jlRE2zO0qA3OCwKmbEcp4WLNpu5eq7TR15f9qNJ+ST\n\tPW/A==","X-Gm-Message-State":"AOAM5337jxnVsa1LESXzQ+HJ9XdSfg0gjTIuZ+JXnzwKKym8wEAkIHpP\n\tsMAFp8zGo385UyO6qD0jDsQFXLY0XNJhqTD7DeeB1R4wisfHG81m3CHwYSfTZItaB0ULTExu+CB\n\tCVjpuXEN2tfBVyTmpd59EVqjbA1psE+kHf2f0Q+Cl0n6flizWZA==","X-Received":["by 2002:a54:4007:0:b0:326:58cf:a259 with SMTP id\n\tx7-20020a544007000000b0032658cfa259mr13563485oie.251.1652187594893; \n\tTue, 10 May 2022 05:59:54 -0700 (PDT)","by 2002:a54:4007:0:b0:326:58cf:a259 with SMTP id\n\tx7-20020a544007000000b0032658cfa259mr13563451oie.251.1652187593901;\n\tTue, 10 May 2022 05:59:53 -0700 (PDT)"],"X-Google-Smtp-Source":"ABdhPJx+euphAiUGx9vdr6T6qneQmZXgUCwBGUdJGS+vxa6y5lAYuqt4gAbOoO+QlXSQrX7r84TZ6QsMbAtRTc5Cdvw=","MIME-Version":"1.0","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>","In-Reply-To":"<165217739174.2416244.12652784382118148891@Monstersaurus>","Date":"Tue, 10 May 2022 13:59:37 +0100","Message-ID":"<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","X-Mimecast-Spam-Score":"0","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Eric Curtin via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Eric Curtin <ecurtin@redhat.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22933,"web_url":"https://patchwork.libcamera.org/comment/22933/","msgid":"<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>","date":"2022-05-10T13:04:57","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Eric,\n\nOn Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n> > Quoting Naushir Patuck (2022-05-10 10:49:14)\n> > > On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n> > >\n> > > > This is just a conversation starter, not for merging. I really like\n> > > > libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > > > is the first library I add to any new C++ project I make.\n> > > >\n> > > > You can find more information about libfmt from:\n> > > >\n> > > > https://github.com/fmtlib/fmt\n> > > > https://fmt.dev/latest/index.html\n> > > >\n> > > > This patch is just a crude conversion with ugly macros to showcase what\n> > > > the formatting code might look like.\n> > > >\n> > > > libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > > > than iostreams, but for the size it didn't seem to be true in cam's case\n> > > > as the tests below show. However, simple prints did reduce the exe size,\n> > > > but the few more complex ones increased the size.\n> > > >\n> > > > Size tests with gcc 11.2.0-19ubuntu1\n> > > >\n> > > > - Without libfmt\n> > > >\n> > > > debug           3523400\n> > > > debug lto       3269368\n> > > > release         223056\n> > > > release lto     172280\n> > > >\n> > > > - With libfmt\n> > > >\n> > > > debug           4424256\n> > > > debug lto       4143840\n> > > > release         303952\n> > > > release lto     252640\n> > > >\n> > > > Above shows that cam's size clearly increases with libfmt. However, the\n> > > > increase really comes only from one case, the use of fmt::memory_buffer\n> > > > and std::back_inserter. Converting that code to use fmt::format() and\n> > > > naive string append gives:\n> > > >\n> > > > release with string append      233680\n> > > > release lto with string append  186936\n> > > >\n> > > > Also, if I add another use of fmt::memory_buffer and std::back_inserter\n> > > > to another file, I see much less increase in the size:\n> > > >\n> > > > release lto with two uses of memory_buffer, back_inserter       256736\n> > > >\n> > > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> > >\n> > > For what it's worth, I absolutely loathe formatting in std iostream, and\n> > > libfmt is a wonderful alternative.\n> > > It also is close enough to the C++20 std::format implementation that\n> > > eventual porting would be low effort.  So I am all for this change :)\n\nIt's going to take a while before we can switch to C++20, but I'm\nlooking forward to it (and before that to some of the C++17 features\ntoo, it would be nice to use std::optional in the public API).\n\n> I am all in for this change also, personally I would have changed to\n> printf for now\n> to have one less dependency (also an easy port to C++20 std::format). The\n> dependency list is getting large, I noticed a build of mine failing\n> recently because\n> I didn't have libyaml.\n\nI've posted a patch that falls back to a subproject in that case. Would\nyou be able to give it a try ?\n\n> But std::format and libfmt are quite fast and anything is better than\n> streams so +1.\n> \n> > I've never used (yet) {fmt}, but I've only heard good things about\n> > performance, and of course it's headed into the standard, so I also\n> > think there is some good merit to be found in this development.","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 0AC8EC0F2A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 13:05:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3632765646;\n\tTue, 10 May 2022 15:05:05 +0200 (CEST)","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 181C965643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 15:05:03 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 74557B60;\n\tTue, 10 May 2022 15:05:02 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652187905;\n\tbh=G1GiN0CBtRZWiEiC4kBnUErKX7AyepnV9RgdrzyZXoo=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=rcH9E/K7B6pTxIPED4OpDWXHCZ8pxGNvq/KkIa6eM5oTxkUYOaoKYJeVYPiAqZ0YP\n\thyV3UZOskSyvJoQ83ge1GF2JzIqBm8sXd5aej2qNvObHagM+OfcBmCl6sRhgfX1UNf\n\tWxPO/HOMvF0yz/e1oBQ8qrKmJnF291KPPI2obz6P2Ur+rQ8dEOB45/AGh8oPPbz6HT\n\tpdPxS4XfJvGgVLF557UcuxojoOimcIznPYWJOn5Dq7bAjHHcehS0ct2nBqZjPBcpeN\n\tb+bND3xHbnpYQs8IKEf2DqWAX2kvJ8RfATJiF734SL+F2fieKgW1hBeHV7LZkR1HKt\n\txvxqRSRSB+0ew==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652187902;\n\tbh=G1GiN0CBtRZWiEiC4kBnUErKX7AyepnV9RgdrzyZXoo=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=W6/pqF+v0QXdDLnBfxslPHOUy99gHZ1Q2N2ZbMDQNR8StWxvcRVC/sK8sC6gtG+8S\n\tyXXTdSLBc1tF0h1lBWPN4Iu5TSMiUn0N39uvOmw9eGmS6eebaYh41vlJFDKm/i6NiO\n\t9qs8fqxrV0j83ymhjHdidTPwEIXZ26CZ1XC416+U="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"W6/pqF+v\"; dkim-atps=neutral","Date":"Tue, 10 May 2022 16:04:57 +0300","To":"Eric Curtin <ecurtin@redhat.com>","Message-ID":"<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>\n\t<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22934,"web_url":"https://patchwork.libcamera.org/comment/22934/","msgid":"<Ynpk3kNClJuTNvQV@pendragon.ideasonboard.com>","date":"2022-05-10T13:13:02","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Tomi,\n\nOn Tue, May 10, 2022 at 12:10:21PM +0300, Tomi Valkeinen wrote:\n> On 10/05/2022 10:16, Tomi Valkeinen wrote:\n> > This is just a conversation starter, not for merging. I really like\n> > libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > is the first library I add to any new C++ project I make.\n> > \n> > You can find more information about libfmt from:\n> > \n> > https://github.com/fmtlib/fmt\n> > https://fmt.dev/latest/index.html\n> > \n> > This patch is just a crude conversion with ugly macros to showcase what\n> > the formatting code might look like.\n> \n> As for the PR and EPR macros, those was just something to get forward \n> with. I think fmt::print(\"\") is fine, but fmt::print(stderr, \"\") is a \n> bit long.\n> \n> Compared to cout, \"fmt::print(\" is actually shorter than \"std::cout << \n> \", and without all those << and std::endl the lines are shorter.\n> \n> Not so with cerr and fmt::print(stderr, (although in many cases the \n> total length would still be shorter) but I think it makes sense to have \n> a macro/inline func for error prints, if only to make it more obvious \n> that it's an error print.\n> \n> And, of course, libfmt could also be used for logging:\n> \n> LOG(Camera, Error) << \"Camera in \" << camera_state_names[currentState]\n> \t\t   << \" state trying \" << from << \"() requiring state \"\n> \t\t   << camera_state_names[state];\n> \n> to\n> \n> LOG(Camera, Error, \"Camera in {} state trying {}() requiring state {}\",\n>      camera_state_names[currentState], from, camera_state_names[state]);\n\nThat's an interesting example, it clearly shows one of the main semantic\ndifferences between the two options. std::format makes it easier to see\nthe whole message as it's stored in a single string, but the drawback is\nthat you have to go back and forth between the format string and the\narguments to figure out what is getting printed. std::ostream makes the\nreading flow more natural in my opinion.\n\nI'm pretty sure my preference will vary based on the logging statement,\nsome will become more readable, others will get worse. That won't help\nmaking a decision :-) It would be nice, however, to see how it would\naffect logging, with a sample patch for the logger. It should ideally\nalso add a custom formatter for at least one of the geometry classes.\n\nThis being said, we don't have to switch to libfmt (assuming we want to\ndo so) everywhere, or everywhere at the same time. The cam application\nand the unit tests could possibly go first, as they make heavy use of\nstd::cout and std::cerr (for the unit tests it would actually be nice to\nhave a better form of status reporting, std::cerr may be best replaced\nwith assertions that raise exceptions).","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 51C46C3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 13:13:10 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A0A0265646;\n\tTue, 10 May 2022 15:13:09 +0200 (CEST)","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 4E91265643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 15:13:08 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8E4FAB60;\n\tTue, 10 May 2022 15:13:07 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652188389;\n\tbh=uNbxe5GcUeQyvcrdVcL+SrO3XkDOtX9ffzhRByMyWNQ=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=wG9gV2Bi+w417fJM2ayNNa0UEhr93WGpFCSFTsGKmRNlc2/07oaGt7c3nP3/uRbwZ\n\tiNMEZt+DW0AvODL1sQtuyyv13G5PQLI/cIGQFXwaGLmSGrYBdlrCl2LM5tKSMCEjhK\n\t99DkSZwWLuGS9h8BoVn3QnhtIb+GIlnL+fLwxLu5/+0Vg1VpBAh1zBMYrkK8+C4Bhy\n\t3nbxvk0VhF0mlPieijsvaXWPY8H6wb4PxPbU4CpYf2hAkabYThwJiswMiwxrnhTy0y\n\t5qMdBkTyXWQM/dja8EP4GlmYkrmwtntUkNldh4dJQpN3CxoW67GUqm/+sCjMKzsdHK\n\tQqBQrKqSGDu9g==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652188387;\n\tbh=uNbxe5GcUeQyvcrdVcL+SrO3XkDOtX9ffzhRByMyWNQ=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=BXh0xZXKerJCPUZY3xyUD71d8b9iUJCFbc29/Cx1rWxFdlrUH+KVS4SWa5MVxKGuT\n\t+O0+oFuSwHxlXMN+OmCdbamBU/91ugIGk29snlyek4wog02Wqz2yE6RfxBVCRf5HB4\n\t8shs1mMUFjSKZPn4/ehdAHKvMV5A1BlkLpt5gny4="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"BXh0xZXK\"; dkim-atps=neutral","Date":"Tue, 10 May 2022 16:13:02 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<Ynpk3kNClJuTNvQV@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<ccc4c7a3-ddd4-7a39-9f9d-5b328b15f6b4@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<ccc4c7a3-ddd4-7a39-9f9d-5b328b15f6b4@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22935,"web_url":"https://patchwork.libcamera.org/comment/22935/","msgid":"<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>","date":"2022-05-10T13:22:49","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Tomi,\n\nThank you for the patch.\n\nOn Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n> This is just a conversation starter, not for merging. I really like\n> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> is the first library I add to any new C++ project I make.\n\nYou've got it wrong. The first library you should add to any new C++\nproject you make is libcamera :-)\n\n> You can find more information about libfmt from:\n> \n> https://github.com/fmtlib/fmt\n> https://fmt.dev/latest/index.html\n> \n> This patch is just a crude conversion with ugly macros to showcase what\n> the formatting code might look like.\n> \n> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> than iostreams, but for the size it didn't seem to be true in cam's case\n> as the tests below show. However, simple prints did reduce the exe size,\n> but the few more complex ones increased the size.\n> \n> Size tests with gcc 11.2.0-19ubuntu1\n> \n> - Without libfmt\n> \n> debug\t\t3523400\n> debug lto\t3269368\n> release\t\t223056\n> release lto\t172280\n> \n> - With libfmt\n> \n> debug\t\t4424256\n> debug lto\t4143840\n> release\t\t303952\n> release lto\t252640\n> \n> Above shows that cam's size clearly increases with libfmt. However, the\n> increase really comes only from one case, the use of fmt::memory_buffer\n> and std::back_inserter.\n\nIs this because libfmt is a header-only library ? What if it is built as\na shared object, what's the impact on code size in cam ?\n\n> Converting that code to use fmt::format() and\n> naive string append gives:\n> \n> release with string append\t233680\n> release lto with string append\t186936\n\nWhat's the impact of switching to string concatenation on runtime ?\n\nWould you consider the option of keeping std::stringstream ?\n\n> Also, if I add another use of fmt::memory_buffer and std::back_inserter\n> to another file, I see much less increase in the size:\n> \n> release lto with two uses of memory_buffer, back_inserter\t256736\n> \n> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> ---\n>  src/cam/camera_session.cpp | 105 ++++++++++++++++---------------------\n>  src/cam/drm.cpp            |  68 ++++++++----------------\n>  src/cam/event_loop.cpp     |   9 ++--\n>  src/cam/file_sink.cpp      |  31 +++++------\n>  src/cam/image.cpp          |  15 +++---\n>  src/cam/kms_sink.cpp       |  38 ++++++--------\n>  src/cam/main.cpp           |  28 +++++-----\n>  src/cam/meson.build        |   5 +-\n>  src/cam/options.cpp        |  66 ++++++++++-------------\n>  src/cam/stream_options.cpp |  18 +++----\n>  10 files changed, 165 insertions(+), 218 deletions(-)\n\n[snip]","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 6EFBBC0F2A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 13:22:58 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id ADA2C65644;\n\tTue, 10 May 2022 15:22:57 +0200 (CEST)","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 755B865643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 15:22:55 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id D11394A8;\n\tTue, 10 May 2022 15:22:54 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652188977;\n\tbh=YDPpIJIczoTRnSd57bjSAW5tuyJgvcW6IAfvuBCucxM=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=V6E6WTLPrXi7mkqANMKlEy7u3kZJFaPP0rTlcoPP44Aq0Wmrz2KKKJ+f1Glh7SY+a\n\t0i9fNfclwve3zBnAs2nlev5j7HnAHrsWlovlGB+z8DuJSu3fcJNWy4qRtgYFjFjnBw\n\tQZFvxnVSyt/4zEbZQ/5bE7KIlFHlSZqdURiG6WFHjw+6sQGfgRi9rIjvcHbrKCbnrC\n\tFjFLNhih1GlPiSJ7BVCjg7osVDZeVZ/M6XM1L2V/s9ZDT3CJVNbM3ZcrlTvzYQ864W\n\t26aaNg+ZbOUpb3Wvomk73PqubjuXRzL0WEYEYi1tBGmpdGJSwAhrUgXV6JP+I1i3WV\n\tSMOOZcCw/HTmQ==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652188975;\n\tbh=YDPpIJIczoTRnSd57bjSAW5tuyJgvcW6IAfvuBCucxM=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=OGRxhwAls8oMqShys6gp33CavFfsDuVVqIkxImj35EujmxqY6QA9N0f+kWZ+qrp45\n\t+wZTNn/ip/UTxvXvGpHjd850DQ9BXTKFmhER5iY4H9RwNtbA88lPBSVmhmjUNNMtBr\n\tm6QVskYz7QqK5dPRO1IbReWnKs1Ba2/OJLTOieQc="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"OGRxhwAl\"; dkim-atps=neutral","Date":"Tue, 10 May 2022 16:22:49 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22936,"web_url":"https://patchwork.libcamera.org/comment/22936/","msgid":"<YnpncDAsMSUOcyzY@pendragon.ideasonboard.com>","date":"2022-05-10T13:24:00","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Tue, May 10, 2022 at 04:22:51PM +0300, Laurent Pinchart wrote:\n> Hi Tomi,\n> \n> Thank you for the patch.\n> \n> On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n> > This is just a conversation starter, not for merging. I really like\n> > libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > is the first library I add to any new C++ project I make.\n> \n> You've got it wrong. The first library you should add to any new C++\n> project you make is libcamera :-)\n> \n> > You can find more information about libfmt from:\n> > \n> > https://github.com/fmtlib/fmt\n> > https://fmt.dev/latest/index.html\n> > \n> > This patch is just a crude conversion with ugly macros to showcase what\n> > the formatting code might look like.\n> > \n> > libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > than iostreams, but for the size it didn't seem to be true in cam's case\n> > as the tests below show. However, simple prints did reduce the exe size,\n> > but the few more complex ones increased the size.\n> > \n> > Size tests with gcc 11.2.0-19ubuntu1\n> > \n> > - Without libfmt\n> > \n> > debug\t\t3523400\n> > debug lto\t3269368\n> > release\t\t223056\n> > release lto\t172280\n> > \n> > - With libfmt\n> > \n> > debug\t\t4424256\n> > debug lto\t4143840\n> > release\t\t303952\n> > release lto\t252640\n> > \n> > Above shows that cam's size clearly increases with libfmt. However, the\n> > increase really comes only from one case, the use of fmt::memory_buffer\n> > and std::back_inserter.\n> \n> Is this because libfmt is a header-only library ? What if it is built as\n> a shared object, what's the impact on code size in cam ?\n\nIt would also be useful to make the same measurements using the C++20\nstd::format, as that would be the long term goal.\n\n> > Converting that code to use fmt::format() and\n> > naive string append gives:\n> > \n> > release with string append\t233680\n> > release lto with string append\t186936\n> \n> What's the impact of switching to string concatenation on runtime ?\n> \n> Would you consider the option of keeping std::stringstream ?\n> \n> > Also, if I add another use of fmt::memory_buffer and std::back_inserter\n> > to another file, I see much less increase in the size:\n> > \n> > release lto with two uses of memory_buffer, back_inserter\t256736\n> > \n> > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> > ---\n> >  src/cam/camera_session.cpp | 105 ++++++++++++++++---------------------\n> >  src/cam/drm.cpp            |  68 ++++++++----------------\n> >  src/cam/event_loop.cpp     |   9 ++--\n> >  src/cam/file_sink.cpp      |  31 +++++------\n> >  src/cam/image.cpp          |  15 +++---\n> >  src/cam/kms_sink.cpp       |  38 ++++++--------\n> >  src/cam/main.cpp           |  28 +++++-----\n> >  src/cam/meson.build        |   5 +-\n> >  src/cam/options.cpp        |  66 ++++++++++-------------\n> >  src/cam/stream_options.cpp |  18 +++----\n> >  10 files changed, 165 insertions(+), 218 deletions(-)\n> \n> [snip]","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 81670C0F2A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 13:24:07 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3C01D65646;\n\tTue, 10 May 2022 15:24:07 +0200 (CEST)","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 BA96F65643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 15:24:05 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 565CE4A8;\n\tTue, 10 May 2022 15:24:05 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652189047;\n\tbh=tfIqgQhYamkCguILzOoC5V/XSZPX0v8l+af1naGwNuc=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=PstqpvVsIyoUumuOpaiUQd2nBskGT+wiNAtJtJFVJs/XsJYL2v6DcJ1BGPJQHch/c\n\t/r1sthXWe4/4JUUi4DC4e/bRXwXLpz8LL5TnRk1OkQ5TqHtG1+P7um4IrbNPRN1vrs\n\tyEn96Syv9+BwzcJUZhAqKDoUHAnw73ykKXZvldcZVffll5/f2RDRtaX2E8fbe3yemL\n\tn7G5taHJypvCRmwGFhu6TB9EPkvRHBF8O/XXbQd9MDZMqGyWyv8H4QctnhNyU75gK8\n\tEK4ppU0g1qGNt9lU4XlijREIWqxyUtfWyGW53gLIXh80yae8ndK8dV4X2xvLTyJmtl\n\tp/stWhDeYgEtg==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652189045;\n\tbh=tfIqgQhYamkCguILzOoC5V/XSZPX0v8l+af1naGwNuc=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=eGrV7QlYnCIwvOTWKCq9AmjNAWYo6nIGL7iMb9zeZSZkJnRbXn8yn5/e88YatdEDp\n\tq3NfoiRA1gYPPHim0cuZzkNn3+vdLmVxjVetBjTSXGxCu37qRXs6KKM5QHdQtAFCfQ\n\tYeiUVaIkvEZf3vOkvXUX3hJNtt9PP7hz+74ku4D4="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"eGrV7QlY\"; dkim-atps=neutral","Date":"Tue, 10 May 2022 16:24:00 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<YnpncDAsMSUOcyzY@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22937,"web_url":"https://patchwork.libcamera.org/comment/22937/","msgid":"<90fdfe55-c27a-9251-4e26-570e406a1fbe@ideasonboard.com>","date":"2022-05-10T13:47:56","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":109,"url":"https://patchwork.libcamera.org/api/people/109/","name":"Tomi Valkeinen","email":"tomi.valkeinen@ideasonboard.com"},"content":"On 10/05/2022 16:13, Laurent Pinchart wrote:\n> Hi Tomi,\n> \n> On Tue, May 10, 2022 at 12:10:21PM +0300, Tomi Valkeinen wrote:\n>> On 10/05/2022 10:16, Tomi Valkeinen wrote:\n>>> This is just a conversation starter, not for merging. I really like\n>>> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n>>> is the first library I add to any new C++ project I make.\n>>>\n>>> You can find more information about libfmt from:\n>>>\n>>> https://github.com/fmtlib/fmt\n>>> https://fmt.dev/latest/index.html\n>>>\n>>> This patch is just a crude conversion with ugly macros to showcase what\n>>> the formatting code might look like.\n>>\n>> As for the PR and EPR macros, those was just something to get forward\n>> with. I think fmt::print(\"\") is fine, but fmt::print(stderr, \"\") is a\n>> bit long.\n>>\n>> Compared to cout, \"fmt::print(\" is actually shorter than \"std::cout <<\n>> \", and without all those << and std::endl the lines are shorter.\n>>\n>> Not so with cerr and fmt::print(stderr, (although in many cases the\n>> total length would still be shorter) but I think it makes sense to have\n>> a macro/inline func for error prints, if only to make it more obvious\n>> that it's an error print.\n>>\n>> And, of course, libfmt could also be used for logging:\n>>\n>> LOG(Camera, Error) << \"Camera in \" << camera_state_names[currentState]\n>> \t\t   << \" state trying \" << from << \"() requiring state \"\n>> \t\t   << camera_state_names[state];\n>>\n>> to\n>>\n>> LOG(Camera, Error, \"Camera in {} state trying {}() requiring state {}\",\n>>       camera_state_names[currentState], from, camera_state_names[state]);\n> \n> That's an interesting example, it clearly shows one of the main semantic\n> differences between the two options. std::format makes it easier to see\n> the whole message as it's stored in a single string, but the drawback is\n> that you have to go back and forth between the format string and the\n> arguments to figure out what is getting printed. std::ostream makes the\n> reading flow more natural in my opinion.\n\nI think the bigger difference comes apparent when using more complex \nformats than just plain {} (i.e. setting precision, width, alignment, etc).\n\n> I'm pretty sure my preference will vary based on the logging statement,\n> some will become more readable, others will get worse. That won't help\n> making a decision :-) It would be nice, however, to see how it would\n\nMaybe there's a logging statement out there which is more readable with \nstreams, but I'm not aware of it ;).\n\n> affect logging, with a sample patch for the logger. It should ideally\n> also add a custom formatter for at least one of the geometry classes.\n\nWe already have that in this patch, as camera_session.cpp prints a Size. \nBut it uses the existing std::ostream formatter from geometry.cpp, so... \nit's a bit boring =).\n\nlibfmt has its own formatter support, which is useful if you want to \npass formatting parameters to the formatter. Say, {:#x} and {:.2f} could \nbe used to print \"(0x4, 0x7)\" and \"(4.00, 7.00)\" for the same Size.\n\n  Tomi","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 6F17BC3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 13:48:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id AE3CC65646;\n\tTue, 10 May 2022 15:48:01 +0200 (CEST)","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 C756165643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 15:48:00 +0200 (CEST)","from [192.168.1.111] (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 24F1C4A8;\n\tTue, 10 May 2022 15:48:00 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652190481;\n\tbh=ae8DVLkwpLkzxI9gpAX5l84lGOnOtko6iB7B26wTDPQ=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=qjuOc8wzcQ9Rru4+1QrjeGIPAX20fRTcgMiSxriVMgk/k4puRCq+5nez94W+x3znB\n\tZj1yBC3nnI+EBq7JYAuNtwHv/g+/9CNs40E7U0+QNU7F0wzlrePIcyp+3f6lZusY6P\n\t7dRvWD/eGSXE7R0xutF/SOckpbdyT0mZCFHt++R+2GZoOEkbwRoFidJcigb2nT+jGZ\n\tO/Xl5Tx2PdqtpCmxbQe+U1jhUkAH4SSZlhadsf/CoHL3DNHvO8+e8NNiOXgvxkHxAC\n\toAK85nBIhtg3sso6YVQPRKQoehpLdhnNt5V9LMHKMIuMB1ZwP1CRxbeZP7+ESnKqc5\n\tKhTTM9Mu959VQ==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652190480;\n\tbh=ae8DVLkwpLkzxI9gpAX5l84lGOnOtko6iB7B26wTDPQ=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=WgIBR+f4Xk+fxXsUE/G+S1L6mla8fHpohR9DpQMIEpigHH0PqVSiJQse3C85QjYEC\n\tS/jIi9Lvv+7CiHHqK7dE68cjFpe6DqUliDTfBRCDKRkUvZBl47Y5KHktkx6ZciaNXX\n\tO88h9+UOcghRe9fnmUdOswfPQ/OlGmn1n9NgfLFU="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"WgIBR+f4\"; dkim-atps=neutral","Message-ID":"<90fdfe55-c27a-9251-4e26-570e406a1fbe@ideasonboard.com>","Date":"Tue, 10 May 2022 16:47:56 +0300","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.8.0","Content-Language":"en-US","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<ccc4c7a3-ddd4-7a39-9f9d-5b328b15f6b4@ideasonboard.com>\n\t<Ynpk3kNClJuTNvQV@pendragon.ideasonboard.com>","In-Reply-To":"<Ynpk3kNClJuTNvQV@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Tomi Valkeinen via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22939,"web_url":"https://patchwork.libcamera.org/comment/22939/","msgid":"<Ynpuob11hFoh2fuL@pendragon.ideasonboard.com>","date":"2022-05-10T13:54:41","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Tomi,\n\nOn Tue, May 10, 2022 at 04:47:56PM +0300, Tomi Valkeinen wrote:\n> On 10/05/2022 16:13, Laurent Pinchart wrote:\n> > On Tue, May 10, 2022 at 12:10:21PM +0300, Tomi Valkeinen wrote:\n> >> On 10/05/2022 10:16, Tomi Valkeinen wrote:\n> >>> This is just a conversation starter, not for merging. I really like\n> >>> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> >>> is the first library I add to any new C++ project I make.\n> >>>\n> >>> You can find more information about libfmt from:\n> >>>\n> >>> https://github.com/fmtlib/fmt\n> >>> https://fmt.dev/latest/index.html\n> >>>\n> >>> This patch is just a crude conversion with ugly macros to showcase what\n> >>> the formatting code might look like.\n> >>\n> >> As for the PR and EPR macros, those was just something to get forward\n> >> with. I think fmt::print(\"\") is fine, but fmt::print(stderr, \"\") is a\n> >> bit long.\n> >>\n> >> Compared to cout, \"fmt::print(\" is actually shorter than \"std::cout <<\n> >> \", and without all those << and std::endl the lines are shorter.\n> >>\n> >> Not so with cerr and fmt::print(stderr, (although in many cases the\n> >> total length would still be shorter) but I think it makes sense to have\n> >> a macro/inline func for error prints, if only to make it more obvious\n> >> that it's an error print.\n> >>\n> >> And, of course, libfmt could also be used for logging:\n> >>\n> >> LOG(Camera, Error) << \"Camera in \" << camera_state_names[currentState]\n> >> \t\t   << \" state trying \" << from << \"() requiring state \"\n> >> \t\t   << camera_state_names[state];\n> >>\n> >> to\n> >>\n> >> LOG(Camera, Error, \"Camera in {} state trying {}() requiring state {}\",\n> >>       camera_state_names[currentState], from, camera_state_names[state]);\n> > \n> > That's an interesting example, it clearly shows one of the main semantic\n> > differences between the two options. std::format makes it easier to see\n> > the whole message as it's stored in a single string, but the drawback is\n> > that you have to go back and forth between the format string and the\n> > arguments to figure out what is getting printed. std::ostream makes the\n> > reading flow more natural in my opinion.\n> \n> I think the bigger difference comes apparent when using more complex \n> formats than just plain {} (i.e. setting precision, width, alignment, etc).\n\nOnce you get used to how the precision and other parameters are\nexpressed with libfmt, it's indeed more concise, possibly easier to\nread, and certainly easier to write.\n\n> > I'm pretty sure my preference will vary based on the logging statement,\n> > some will become more readable, others will get worse. That won't help\n> > making a decision :-) It would be nice, however, to see how it would\n> \n> Maybe there's a logging statement out there which is more readable with \n> streams, but I'm not aware of it ;).\n\nI think the above example is more readable with streams :-)\n\n> > affect logging, with a sample patch for the logger. It should ideally\n> > also add a custom formatter for at least one of the geometry classes.\n> \n> We already have that in this patch, as camera_session.cpp prints a Size. \n> But it uses the existing std::ostream formatter from geometry.cpp, so... \n> it's a bit boring =).\n> \n> libfmt has its own formatter support, which is useful if you want to \n> pass formatting parameters to the formatter. Say, {:#x} and {:.2f} could \n> be used to print \"(0x4, 0x7)\" and \"(4.00, 7.00)\" for the same Size.\n\nI meant using custom libfmt formatters, yes, the same way we have custom\noperator<<() for the geometry classes. I'd like to see how that would\nlook like.","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 43E9EC0F2A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 13:54:49 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9351F65646;\n\tTue, 10 May 2022 15:54:48 +0200 (CEST)","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 EC9CF65643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 15:54:46 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 534BE824;\n\tTue, 10 May 2022 15:54:46 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652190888;\n\tbh=W4Mde7yw54viVehWBFCXj+m1V3jYQnH3l4xOS2iFGBc=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=JNbeGPFjTRelMbL04HQQDWiCOhRPBQnlKlFaU2kdQhzLaj0JRuLcFEQeaViblJ9AA\n\tXIuiIGJ3i5V9TJgLbl6d12UG58aEEzwYd0/4nlYU5e4jHxPPBLWBVP3qEWuXH/lDCB\n\tEYrB6dclWzmJEtEDdpEDcH5mbNxkzGlB3x2S7SDaqKeP2LwNj8CLI1eg4+Qzbsc1Cx\n\taxT8SgeUO+lf0PQWsNhTuZS1MS7HpEm6piDKvX0bEaSCE7i7AzOVG+0tI0lQ5n5DMQ\n\teknp5EKIKIpuZqtURWnSEsWzT0kyhbY8jlC4RUPUEQLSWYR/zongIZE2rClEyLSQ6I\n\tHsXhyVlbSXS5w==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652190886;\n\tbh=W4Mde7yw54viVehWBFCXj+m1V3jYQnH3l4xOS2iFGBc=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=eF98NZCjhGx41OUErkesEFj0GYafMUF3R1rLf0tWhBkHpdOeZhjsrAx5/RG3/8Lkq\n\tWtV4PrE9DhIdXU2klqkZGbzfkaMdUgg/vsy3Alz3IaLQFd5qy2/35i3RLM+cM12qFa\n\tYf+bXSpsNsUOKL5Vc74QrxASngaBGXyTfzhV6BqI="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"eF98NZCj\"; dkim-atps=neutral","Date":"Tue, 10 May 2022 16:54:41 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<Ynpuob11hFoh2fuL@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<ccc4c7a3-ddd4-7a39-9f9d-5b328b15f6b4@ideasonboard.com>\n\t<Ynpk3kNClJuTNvQV@pendragon.ideasonboard.com>\n\t<90fdfe55-c27a-9251-4e26-570e406a1fbe@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<90fdfe55-c27a-9251-4e26-570e406a1fbe@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22941,"web_url":"https://patchwork.libcamera.org/comment/22941/","msgid":"<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>","date":"2022-05-10T14:05:05","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":109,"url":"https://patchwork.libcamera.org/api/people/109/","name":"Tomi Valkeinen","email":"tomi.valkeinen@ideasonboard.com"},"content":"On 10/05/2022 16:22, Laurent Pinchart wrote:\n> Hi Tomi,\n> \n> Thank you for the patch.\n> \n> On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n>> This is just a conversation starter, not for merging. I really like\n>> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n>> is the first library I add to any new C++ project I make.\n> \n> You've got it wrong. The first library you should add to any new C++\n> project you make is libcamera :-)\n> \n>> You can find more information about libfmt from:\n>>\n>> https://github.com/fmtlib/fmt\n>> https://fmt.dev/latest/index.html\n>>\n>> This patch is just a crude conversion with ugly macros to showcase what\n>> the formatting code might look like.\n>>\n>> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n>> than iostreams, but for the size it didn't seem to be true in cam's case\n>> as the tests below show. However, simple prints did reduce the exe size,\n>> but the few more complex ones increased the size.\n>>\n>> Size tests with gcc 11.2.0-19ubuntu1\n>>\n>> - Without libfmt\n>>\n>> debug\t\t3523400\n>> debug lto\t3269368\n>> release\t\t223056\n>> release lto\t172280\n>>\n>> - With libfmt\n>>\n>> debug\t\t4424256\n>> debug lto\t4143840\n>> release\t\t303952\n>> release lto\t252640\n>>\n>> Above shows that cam's size clearly increases with libfmt. However, the\n>> increase really comes only from one case, the use of fmt::memory_buffer\n>> and std::back_inserter.\n> \n> Is this because libfmt is a header-only library ? What if it is built as\n> a shared object, what's the impact on code size in cam ?\n\nlibfmt supports header-only, but it's not header-only here.\n\n>> Converting that code to use fmt::format() and\n>> naive string append gives:\n>>\n>> release with string append\t233680\n>> release lto with string append\t186936\n> \n> What's the impact of switching to string concatenation on runtime ?\n\nWhat do you mean?\n\n> Would you consider the option of keeping std::stringstream ?\n\nYou mean formatting mostly with libfmt, but sometimes with streams? Or \nusing libfmt but appending the formatted string to stringstream?\n\n  Tomi","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 A6353C3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 14:05:10 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5296F65646;\n\tTue, 10 May 2022 16:05:10 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E82C365643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 16:05:08 +0200 (CEST)","from [192.168.1.111] (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 62F2FBA9;\n\tTue, 10 May 2022 16:05:08 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652191510;\n\tbh=r0+H1ylgZtA4Ab7T7SEex9eK2I55z2s4FGTveF2wFnw=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=cx6gI+J5shJtTk6ZBqm3SlAwVY/szJkt8YqN9KMupCU9FXCNzJlDHUK7l8Rj45MTU\n\tKzU7yoMfjq/GjZH2dBCT3Nq6a5hNQj9LbeYAj6RhGtsRrhuZfa2fD+Zw8mydSXh97v\n\tLKez7hF+Wz9/tLA7AeFmLmwC8qgOlIyrjwcTfk6a/o6WLLHBTf/h63csJNqikKbGhN\n\tLH8Y5jUk+XSSNX00rvjNTYrOkUmjEWy/xP8ipCzNw1RLoxRJXeGXmIOeQYjmeSjBqL\n\tSziD6458bxQYSuahytFPFZR1J/A5sgCWm8ju/eKbvVXRAwfN2aJm80x3zj5EyM3QRn\n\tOJPl8+pNX1wCg==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652191508;\n\tbh=r0+H1ylgZtA4Ab7T7SEex9eK2I55z2s4FGTveF2wFnw=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=pCCi8bVLNU28ZgE3w9ExCLDGTctE9e43HWHFRO92YJDllwxosSCckfDPKtwLx13lH\n\tJq+J7c3hCFeyMCkyjqSpSMd90ePtSNuhl+uci8/cq8fsw5uXphOgAf9+cTDlxtoEWp\n\tE6bRGRN0wPEF/bdh052DHgea8b0mANvYAOdWqFyE="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"pCCi8bVL\"; dkim-atps=neutral","Message-ID":"<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>","Date":"Tue, 10 May 2022 17:05:05 +0300","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.8.0","Content-Language":"en-US","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>","In-Reply-To":"<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Tomi Valkeinen via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":22942,"web_url":"https://patchwork.libcamera.org/comment/22942/","msgid":"<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>","date":"2022-05-10T14:20:54","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Tomi,\n\nOn Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote:\n> On 10/05/2022 16:22, Laurent Pinchart wrote:\n> > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n> >> This is just a conversation starter, not for merging. I really like\n> >> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> >> is the first library I add to any new C++ project I make.\n> > \n> > You've got it wrong. The first library you should add to any new C++\n> > project you make is libcamera :-)\n> > \n> >> You can find more information about libfmt from:\n> >>\n> >> https://github.com/fmtlib/fmt\n> >> https://fmt.dev/latest/index.html\n> >>\n> >> This patch is just a crude conversion with ugly macros to showcase what\n> >> the formatting code might look like.\n> >>\n> >> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> >> than iostreams, but for the size it didn't seem to be true in cam's case\n> >> as the tests below show. However, simple prints did reduce the exe size,\n> >> but the few more complex ones increased the size.\n> >>\n> >> Size tests with gcc 11.2.0-19ubuntu1\n> >>\n> >> - Without libfmt\n> >>\n> >> debug\t\t3523400\n> >> debug lto\t3269368\n> >> release\t\t223056\n> >> release lto\t172280\n> >>\n> >> - With libfmt\n> >>\n> >> debug\t\t4424256\n> >> debug lto\t4143840\n> >> release\t\t303952\n> >> release lto\t252640\n> >>\n> >> Above shows that cam's size clearly increases with libfmt. However, the\n> >> increase really comes only from one case, the use of fmt::memory_buffer\n> >> and std::back_inserter.\n> > \n> > Is this because libfmt is a header-only library ? What if it is built as\n> > a shared object, what's the impact on code size in cam ?\n> \n> libfmt supports header-only, but it's not header-only here.\n> \n> >> Converting that code to use fmt::format() and\n> >> naive string append gives:\n> >>\n> >> release with string append\t233680\n> >> release lto with string append\t186936\n> > \n> > What's the impact of switching to string concatenation on runtime ?\n> \n> What do you mean?\n\nI mean what is the impact on CPU usage of using the \"naive\" string\nappend, compared to std::stringstream or fmt::memory_buffer ?\n\n> > Would you consider the option of keeping std::stringstream ?\n> \n> You mean formatting mostly with libfmt, but sometimes with streams? Or \n> using libfmt but appending the formatted string to stringstream?\n\nMostly using fmt::, but using std::stringstream without\nfmt::memory_buffer when a string needs to be assembled. I'm not saying\nit's necessarily a good idea, just wondering.","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 DCFCCC3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 10 May 2022 14:21:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0D85A65646;\n\tTue, 10 May 2022 16:21:02 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 49C3E65643\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 10 May 2022 16:21:00 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 99551B60;\n\tTue, 10 May 2022 16:20:59 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1652192462;\n\tbh=gYfhgAkQsPYBfpcpl8gvPpWbx2MXv15SwCR8lns2Rb4=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=OTd0JQsEW2JjDRucEb+JT0wIOgo4bpXamj1t9ZfJNILivgILdUyfTnnS6th04te0I\n\tBfx3G0bb4G4OKKhgho32CJqUxfKsPXcYItUl59v3y1KjqXCFPpfte7GvuTcTEH8f8G\n\tRrqmNpkH5EnK1MG586D0vJzdcf0Rn650gTfI/TDpssbulKRR0wYh4Y8FmGOzFBApZm\n\toWCLz+AaryKYcPCLzHU2wGRZLFSxniG5oN00ZzGkyvli9a+VD7BNpcuW+u7XsRc16a\n\tIy51hNR7IYe5WalEjJ1GHWN8Cp6xCu9mMihuXN3wqZKDMi5ypYdI0kUHx3sbg+2GHX\n\ttQgKVFla0P/tA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1652192459;\n\tbh=gYfhgAkQsPYBfpcpl8gvPpWbx2MXv15SwCR8lns2Rb4=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=PYcbXLKUCU5dHIQpr0zxZyw3b0e0sGdXLvjMTr+wN7YHP4d63cd+MiYnPIEI3Cp4N\n\tnjJlUwJg9xvQpr7LLbmIPStUIeV00hGPA9ZNCh+eJfqtSKLTfIAu8sdlAWrEyA1A5h\n\tnVn+60wY0N0l6RtvQ7EL5jidNGajFDUwPTvA+7MA="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"PYcbXLKU\"; dkim-atps=neutral","Date":"Tue, 10 May 2022 17:20:54 +0300","To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Message-ID":"<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>\n\t<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23120,"web_url":"https://patchwork.libcamera.org/comment/23120/","msgid":"<CAOgh=FzT-AuDvZMKkdXb398xJN7egZevgjXWyh2R=Pqx0gBKMg@mail.gmail.com>","date":"2022-05-21T18:33:03","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":101,"url":"https://patchwork.libcamera.org/api/people/101/","name":"Eric Curtin","email":"ecurtin@redhat.com"},"content":"On Tue, 10 May 2022 at 15:21, Laurent Pinchart via libcamera-devel\n<libcamera-devel@lists.libcamera.org> wrote:\n>\n> Hi Tomi,\n>\n> On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote:\n> > On 10/05/2022 16:22, Laurent Pinchart wrote:\n> > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n> > >> This is just a conversation starter, not for merging. I really like\n> > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > >> is the first library I add to any new C++ project I make.\n> > >\n> > > You've got it wrong. The first library you should add to any new C++\n> > > project you make is libcamera :-)\n> > >\n> > >> You can find more information about libfmt from:\n> > >>\n> > >> https://github.com/fmtlib/fmt\n> > >> https://fmt.dev/latest/index.html\n> > >>\n> > >> This patch is just a crude conversion with ugly macros to showcase what\n> > >> the formatting code might look like.\n> > >>\n> > >> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > >> than iostreams, but for the size it didn't seem to be true in cam's case\n> > >> as the tests below show. However, simple prints did reduce the exe size,\n> > >> but the few more complex ones increased the size.\n> > >>\n> > >> Size tests with gcc 11.2.0-19ubuntu1\n> > >>\n> > >> - Without libfmt\n> > >>\n> > >> debug              3523400\n> > >> debug lto  3269368\n> > >> release            223056\n> > >> release lto        172280\n> > >>\n> > >> - With libfmt\n> > >>\n> > >> debug              4424256\n> > >> debug lto  4143840\n> > >> release            303952\n> > >> release lto        252640\n> > >>\n> > >> Above shows that cam's size clearly increases with libfmt. However, the\n> > >> increase really comes only from one case, the use of fmt::memory_buffer\n> > >> and std::back_inserter.\n> > >\n> > > Is this because libfmt is a header-only library ? What if it is built as\n> > > a shared object, what's the impact on code size in cam ?\n> >\n> > libfmt supports header-only, but it's not header-only here.\n> >\n> > >> Converting that code to use fmt::format() and\n> > >> naive string append gives:\n> > >>\n> > >> release with string append 233680\n> > >> release lto with string append     186936\n> > >\n> > > What's the impact of switching to string concatenation on runtime ?\n> >\n> > What do you mean?\n>\n> I mean what is the impact on CPU usage of using the \"naive\" string\n> append, compared to std::stringstream or fmt::memory_buffer ?\n>\n> > > Would you consider the option of keeping std::stringstream ?\n> >\n> > You mean formatting mostly with libfmt, but sometimes with streams? Or\n> > using libfmt but appending the formatted string to stringstream?\n>\n> Mostly using fmt::, but using std::stringstream without\n> fmt::memory_buffer when a string needs to be assembled. I'm not saying\n> it's necessarily a good idea, just wondering.\n>\n> --\n> Regards,\n>\n> Laurent Pinchart\n>\n\nSomething I wrote which you can use to format C++ strings in a printf-style\n\n```\n  #pragma once\n\n  #include <string.h>\n  #include <string>\n\n  // function that will sprintf to a C++ string starting from\nstd::string::size()\n  // so if you want to completely overwrite a string or start at a\nspecific point\n  // use std::string::clear() or std::string::resize(). str is a std::string.\n  #define STRING_PRINTF(str, ...)                                   \\\n    do {                                                            \\\n      const int size = snprintf(NULL, 0, __VA_ARGS__);              \\\n      const size_t start_of_string = str.size();                    \\\n      str.resize(start_of_string + size);                           \\\n      snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \\\n    } while (0)\n```\n\nThis way you can format C++ strings in a sprintf-style. It would give\nyou many of the benefits of fmt with just a few lines of code\nalternatively. I know we don't only use macros, but it makes sense\nhere because when the compiler see's the snprintf, it warns against\nincorrect format strings when you do it this way.\n\nJust an option...","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 73953BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 21 May 2022 18:33:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A795F65665;\n\tSat, 21 May 2022 20:33:24 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B063A65654\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 21 May 2022 20:33:22 +0200 (CEST)","from mail-qt1-f200.google.com (mail-qt1-f200.google.com\n\t[209.85.160.200]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n\tus-mta-8-Cqsz6flZO0yxDB7oyvMAng-1; Sat, 21 May 2022 14:33:19 -0400","by mail-qt1-f200.google.com with SMTP id\n\tx11-20020ac87ecb000000b002f92dbff915so992007qtj.20\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 21 May 2022 11:33:19 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653158004;\n\tbh=KepXpYAtf9bf0hKN/rfBWIyDM+J8IdfuePLP+r82B7w=;\n\th=References:In-Reply-To:Date:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=pRX7UBufght8XU5uulYpNMDtsJgmirWzAFd6VYO+BZM+Mv5Nt7JjV25v/CSdfkBhD\n\tQgVm0UQwB41S2EB192FUeyclhnP94LUmow4fJvdFdyZ94MlzTAvx36qdftXG38J+2q\n\tjg1oEMub/r2GtHtPXMNmNiKBPtSA70BYV5hPO5AaEypqXiWWiGY64eGUgJO6617fSH\n\tv9tfOYpJ3V0UoC5u0NSRcGN8K1l/D+hN0BnOdfgiZm+G57QEHO1hUYimdRSoL7X5Cd\n\tSo7UNTMQIZKugenrLnX3Yhf96UTGaVKHMS167Zgt1pI7shJt94x/j626kV7P5g+g+T\n\tpi4YaG7aH0LJQ==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1653158001;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=3svyE2dSWW9yrlZLNJAIelk4RWswuwa17fuQTs3Ku2g=;\n\tb=ZEvVtWLEqWR/m5ri+YTYgNgTxGs6S5BKV0eYi8d1hPRLjPV5R43IMz5Y9y/cg9BNkpNzd6\n\tOCtbLWwrI4JYK1PVN0TEwwbbNbm1wnU/7LE7OJ96YxdKnuSqxyMdeEwnWNMrrH9xDIS2Uo\n\tpUbnzvdeInpdK3fPwHBgV6HwHSe5Rxo="],"Authentication-Results":["lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=redhat.com\n\theader.i=@redhat.com header.b=\"ZEvVtWLE\"; \n\tdkim-atps=neutral","relay.mimecast.com;\n\tauth=pass smtp.auth=CUSA124A263 smtp.mailfrom=ecurtin@redhat.com"],"X-MC-Unique":"Cqsz6flZO0yxDB7oyvMAng-1","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=3svyE2dSWW9yrlZLNJAIelk4RWswuwa17fuQTs3Ku2g=;\n\tb=UVzyQCHs3n5F/GcEyCFFLxHl6eNk1uH77dPZ07H73IwnIj+YZ6LYoIsqC5rpW/mUl8\n\tIltf5vXOXvVBEu6g5YJOxa812ASJmVenrOIXiUis1SF23Hg2oaWcIj9ThfoNjFfOPCTe\n\tTAq+fd6bFWIZUPtecBbVG6LqFvqj49PU1n6n/tevSZTtL5b2eFG8h1eJOutkMl+4TdFN\n\tDCHzyDItsaNwINgXtycE5e0RiFRw1cJA6jYErovW9HMJmnfhQryTDPbdmqZuy1onsFcD\n\t4OCEHBMsRMMdHkU6hphU0te7D8y87KVqsdFZQ7v8LT0GheGC9oippxdCJvCNBQFiL6It\n\ted3w==","X-Gm-Message-State":"AOAM533WDgTzeYHAorzCjKNRCMGe50jjVl+KTX9QwWsotN491hGPTldP\n\tpP4qxu5VzCVfO4+6Re4D4Uhh7vTg/uf0tVdUzd61N5CV6ursY5+UW0eXxMmaPlaxsD47b9bWJ63\n\tXwAxo9rwpjZSBlIPHzTfM79nlyoMdM1Hn6n8QxPXg886ua1jdpw==","X-Received":["by 2002:a05:622a:15c8:b0:2f3:d7ae:bae6 with SMTP id\n\td8-20020a05622a15c800b002f3d7aebae6mr11752322qty.106.1653157999024; \n\tSat, 21 May 2022 11:33:19 -0700 (PDT)","by 2002:a05:622a:15c8:b0:2f3:d7ae:bae6 with SMTP id\n\td8-20020a05622a15c800b002f3d7aebae6mr11752315qty.106.1653157998812;\n\tSat, 21 May 2022 11:33:18 -0700 (PDT)"],"X-Google-Smtp-Source":"ABdhPJyXqp6fLFiHxtYi9E92nNBeRVFPIZO/CS+pU0rbsGEu0vy6mtcjRmcWjCceXqAYNKYkDdl/3vrnL7MNm5sXRb8=","MIME-Version":"1.0","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>\n\t<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>\n\t<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>","In-Reply-To":"<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>","Date":"Sat, 21 May 2022 19:33:03 +0100","Message-ID":"<CAOgh=FzT-AuDvZMKkdXb398xJN7egZevgjXWyh2R=Pqx0gBKMg@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","X-Mimecast-Spam-Score":"0","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Eric Curtin via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Eric Curtin <ecurtin@redhat.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23122,"web_url":"https://patchwork.libcamera.org/comment/23122/","msgid":"<YooJVH/6UU9HmOje@pendragon.ideasonboard.com>","date":"2022-05-22T09:58:44","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Eric,\n\nOn Sat, May 21, 2022 at 07:33:03PM +0100, Eric Curtin wrote:\n> On Tue, 10 May 2022 at 15:21, Laurent Pinchart wrote:\n> > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote:\n> > > On 10/05/2022 16:22, Laurent Pinchart wrote:\n> > > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n> > > >> This is just a conversation starter, not for merging. I really like\n> > > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > > >> is the first library I add to any new C++ project I make.\n> > > >\n> > > > You've got it wrong. The first library you should add to any new C++\n> > > > project you make is libcamera :-)\n> > > >\n> > > >> You can find more information about libfmt from:\n> > > >>\n> > > >> https://github.com/fmtlib/fmt\n> > > >> https://fmt.dev/latest/index.html\n> > > >>\n> > > >> This patch is just a crude conversion with ugly macros to showcase what\n> > > >> the formatting code might look like.\n> > > >>\n> > > >> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > > >> than iostreams, but for the size it didn't seem to be true in cam's case\n> > > >> as the tests below show. However, simple prints did reduce the exe size,\n> > > >> but the few more complex ones increased the size.\n> > > >>\n> > > >> Size tests with gcc 11.2.0-19ubuntu1\n> > > >>\n> > > >> - Without libfmt\n> > > >>\n> > > >> debug              3523400\n> > > >> debug lto  3269368\n> > > >> release            223056\n> > > >> release lto        172280\n> > > >>\n> > > >> - With libfmt\n> > > >>\n> > > >> debug              4424256\n> > > >> debug lto  4143840\n> > > >> release            303952\n> > > >> release lto        252640\n> > > >>\n> > > >> Above shows that cam's size clearly increases with libfmt. However, the\n> > > >> increase really comes only from one case, the use of fmt::memory_buffer\n> > > >> and std::back_inserter.\n> > > >\n> > > > Is this because libfmt is a header-only library ? What if it is built as\n> > > > a shared object, what's the impact on code size in cam ?\n> > >\n> > > libfmt supports header-only, but it's not header-only here.\n> > >\n> > > >> Converting that code to use fmt::format() and\n> > > >> naive string append gives:\n> > > >>\n> > > >> release with string append 233680\n> > > >> release lto with string append     186936\n> > > >\n> > > > What's the impact of switching to string concatenation on runtime ?\n> > >\n> > > What do you mean?\n> >\n> > I mean what is the impact on CPU usage of using the \"naive\" string\n> > append, compared to std::stringstream or fmt::memory_buffer ?\n> >\n> > > > Would you consider the option of keeping std::stringstream ?\n> > >\n> > > You mean formatting mostly with libfmt, but sometimes with streams? Or\n> > > using libfmt but appending the formatted string to stringstream?\n> >\n> > Mostly using fmt::, but using std::stringstream without\n> > fmt::memory_buffer when a string needs to be assembled. I'm not saying\n> > it's necessarily a good idea, just wondering.\n> \n> Something I wrote which you can use to format C++ strings in a printf-style\n> \n> ```\n>   #pragma once\n> \n>   #include <string.h>\n>   #include <string>\n> \n>   // function that will sprintf to a C++ string starting from std::string::size()\n>   // so if you want to completely overwrite a string or start at a specific point\n>   // use std::string::clear() or std::string::resize(). str is a std::string.\n>   #define STRING_PRINTF(str, ...)                                   \\\n>     do {                                                            \\\n>       const int size = snprintf(NULL, 0, __VA_ARGS__);              \\\n>       const size_t start_of_string = str.size();                    \\\n>       str.resize(start_of_string + size);                           \\\n>       snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \\\n>     } while (0)\n> ```\n> \n> This way you can format C++ strings in a sprintf-style. It would give\n> you many of the benefits of fmt with just a few lines of code\n> alternatively. I know we don't only use macros, but it makes sense\n> here because when the compiler see's the snprintf, it warns against\n> incorrect format strings when you do it this way.\n\nBeside the fact that snprintf() is called twice which may be less\nefficient (although maybe still more efficient than libfmt), the trouble\nhere is that __VA_ARGS__ will be evaluated twice.\n\n\tunsigned int index;\n\tstd::string str;\n\n\tindex = 0;\n\tSTRING_PRINTF(str, \"%u\", index++);\n\t/* Now index is equal to 2 */\n\n> Just an option...","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 64721BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 22 May 2022 09:58:52 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BA2A565663;\n\tSun, 22 May 2022 11:58:51 +0200 (CEST)","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 A3CED60422\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 22 May 2022 11:58:49 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(static-11-127-145-212.ipcom.comunitel.net [212.145.127.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2AFC1563;\n\tSun, 22 May 2022 11:58:49 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653213531;\n\tbh=6ZsuHWfNEnq0OOlMxGO184m8Q3NNXf34ByQFwXsH/u4=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=j97IdEV+P3iBvZzScsyovqnq0wnQJQ4f+SpxjZ4pkpH5u7hLhlC9m9NbQtowxkuDm\n\tXHkS/i1g8EUA00p+ZmuSgUt9uac4eWqyFIjDdmOc2r3spR/FUtj2X8RnS712PVEw75\n\tVFT+mbGKzUdJm8EoqhDQcco7Kc8aDqLUVCYxhTH6/IINFYyJDRRrhzL57VZj27UDM9\n\tJyNu1u74sLMTfvEuEwT1BsSRPO07cj5WHoCClfXEGviuTSnngTYoUpHHx9FXTY4yyb\n\tymjtQTGAVafFGhm7uNOluGIpRgJB2Lw9HHxgCi8KaBpet6+b7Y1aHldMmk+psC07pP\n\tz3p8PdcuioaEg==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653213529;\n\tbh=6ZsuHWfNEnq0OOlMxGO184m8Q3NNXf34ByQFwXsH/u4=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=qVqkJrQtKzEykwe7WtCK2JIBOuFt9fXlU8/uyxIUDmMG3uipl9nf4xwfbHcZI3vgl\n\tlakSYFYDRnlDK2KdY4XIdLjNbMoN7kbhWN9WgC2oO8qZEvCoKs4ulQEKku47I8P3B6\n\tHWcmjU+QjOMQy8XZqx/cveKjNXX7hMq2m030R+WE="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"qVqkJrQt\"; dkim-atps=neutral","Date":"Sun, 22 May 2022 12:58:44 +0300","To":"Eric Curtin <ecurtin@redhat.com>","Message-ID":"<YooJVH/6UU9HmOje@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>\n\t<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>\n\t<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>\n\t<CAOgh=FzT-AuDvZMKkdXb398xJN7egZevgjXWyh2R=Pqx0gBKMg@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<CAOgh=FzT-AuDvZMKkdXb398xJN7egZevgjXWyh2R=Pqx0gBKMg@mail.gmail.com>","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23124,"web_url":"https://patchwork.libcamera.org/comment/23124/","msgid":"<165321410734.4128907.16683618909815271160@Monstersaurus>","date":"2022-05-22T10:08:27","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Laurent Pinchart (2022-05-10 14:04:57)\n> Hi Eric,\n> \n> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n> > On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n> > > Quoting Naushir Patuck (2022-05-10 10:49:14)\n> > > > On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n> > > >\n> > > > > This is just a conversation starter, not for merging. I really like\n> > > > > libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > > > > is the first library I add to any new C++ project I make.\n> > > > >\n> > > > > You can find more information about libfmt from:\n> > > > >\n> > > > > https://github.com/fmtlib/fmt\n> > > > > https://fmt.dev/latest/index.html\n> > > > >\n> > > > > This patch is just a crude conversion with ugly macros to showcase what\n> > > > > the formatting code might look like.\n> > > > >\n> > > > > libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > > > > than iostreams, but for the size it didn't seem to be true in cam's case\n> > > > > as the tests below show. However, simple prints did reduce the exe size,\n> > > > > but the few more complex ones increased the size.\n> > > > >\n> > > > > Size tests with gcc 11.2.0-19ubuntu1\n> > > > >\n> > > > > - Without libfmt\n> > > > >\n> > > > > debug           3523400\n> > > > > debug lto       3269368\n> > > > > release         223056\n> > > > > release lto     172280\n> > > > >\n> > > > > - With libfmt\n> > > > >\n> > > > > debug           4424256\n> > > > > debug lto       4143840\n> > > > > release         303952\n> > > > > release lto     252640\n> > > > >\n> > > > > Above shows that cam's size clearly increases with libfmt. However, the\n> > > > > increase really comes only from one case, the use of fmt::memory_buffer\n> > > > > and std::back_inserter. Converting that code to use fmt::format() and\n> > > > > naive string append gives:\n> > > > >\n> > > > > release with string append      233680\n> > > > > release lto with string append  186936\n> > > > >\n> > > > > Also, if I add another use of fmt::memory_buffer and std::back_inserter\n> > > > > to another file, I see much less increase in the size:\n> > > > >\n> > > > > release lto with two uses of memory_buffer, back_inserter       256736\n> > > > >\n> > > > > Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>\n> > > >\n> > > > For what it's worth, I absolutely loathe formatting in std iostream, and\n> > > > libfmt is a wonderful alternative.\n> > > > It also is close enough to the C++20 std::format implementation that\n> > > > eventual porting would be low effort.  So I am all for this change :)\n> \n> It's going to take a while before we can switch to C++20, but I'm\n> looking forward to it (and before that to some of the C++17 features\n> too, it would be nice to use std::optional in the public API).\n\n\nSo, I've just seen as Christian said - we already do!\n\nIt's used by color space ..\n\n\nSo we're faced with either reverting, or rolling back on the color-space\nchange, or pushing forwards with C++17 on the public-api.\n\nMaybe this deserves it's own thread...\n\n--\nKieran\n\n\n> \n> > I am all in for this change also, personally I would have changed to\n> > printf for now\n> > to have one less dependency (also an easy port to C++20 std::format). The\n> > dependency list is getting large, I noticed a build of mine failing\n> > recently because\n> > I didn't have libyaml.\n> \n> I've posted a patch that falls back to a subproject in that case. Would\n> you be able to give it a try ?\n> \n> > But std::format and libfmt are quite fast and anything is better than\n> > streams so +1.\n> > \n> > > I've never used (yet) {fmt}, but I've only heard good things about\n> > > performance, and of course it's headed into the standard, so I also\n> > > think there is some good merit to be found in this development.\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 CF7BDBD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 22 May 2022 10:08:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 84EC565663;\n\tSun, 22 May 2022 12:08:31 +0200 (CEST)","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 AC61460419\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 22 May 2022 12:08:29 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 66C91563;\n\tSun, 22 May 2022 12:08:29 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653214111;\n\tbh=TSWB7xoKv4j2QmtSotH6DchjzQYVX1PdSU1XXj2YqUk=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=buFL2r2yQPNvYNE3tlw9JQzS/P0KG1RUd2L/WZVC+a0d6b4Tedcqq05K9pUjFJUKR\n\tIGoRi1EpokuNb7xcn+HJnNbO2nLOjYiTXGzG7cCw2lG3/+uPPqGB+1EzBftzmz6Ud4\n\tWS5ahIHuWI6Xi3ZYAbA5zQpr3ZQcQVLkwN8FRVASz37wJK23hQJyI7k7PIEVVB7Wfn\n\t+mJ65t/nxQnnNgA3cRWcLe1j00kcqRi5LHLxWe/K0UysZFHe2VyHHsLOk0kQmURc6y\n\tv3Nh59k7lLIED9fOEvLZv4YG6OLJAL7JS4DKJsjUI69aE0Ctg+411Zwd4RtQjpA2bo\n\tYliVjWWXb1b2Q==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653214109;\n\tbh=TSWB7xoKv4j2QmtSotH6DchjzQYVX1PdSU1XXj2YqUk=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=A1h1Zsl8YphCv5SfQOFPpfY2ZwQoARxwZcXDU2AwxInqzpmdJdHR8MJb77o2+dQx8\n\tH2gBOU3G0ve/LFy+7WRhiZWH2d/qcQNegDhGDgt8MrBFkwZsTqxRXQ18/w44wEsElf\n\t6wht26nY7vCDodTPC6LYnBofAiqdFXGqDem5qTiM="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"A1h1Zsl8\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>\n\t<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>\n\t<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>","To":"Eric Curtin <ecurtin@redhat.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Sun, 22 May 2022 11:08:27 +0100","Message-ID":"<165321410734.4128907.16683618909815271160@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23126,"web_url":"https://patchwork.libcamera.org/comment/23126/","msgid":"<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>","date":"2022-05-22T10:52:19","subject":"[libcamera-devel] Usage of C++17 in the public API (was \"[PATCH]\n\tcam: convert to libfmt\")","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote:\n> Quoting Laurent Pinchart (2022-05-10 14:04:57)\n> > On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n> > > On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n> > > > Quoting Naushir Patuck (2022-05-10 10:49:14)\n> > > > > On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n\n[snip]\n\n> > > > > For what it's worth, I absolutely loathe formatting in std iostream, and\n> > > > > libfmt is a wonderful alternative.\n> > > > > It also is close enough to the C++20 std::format implementation that\n> > > > > eventual porting would be low effort.  So I am all for this change :)\n> > \n> > It's going to take a while before we can switch to C++20, but I'm\n> > looking forward to it (and before that to some of the C++17 features\n> > too, it would be nice to use std::optional in the public API).\n> \n> \n> So, I've just seen as Christian said - we already do!\n> \n> It's used by color space ..\n\nOops...\n\n> So we're faced with either reverting, or rolling back on the color-space\n> change, or pushing forwards with C++17 on the public-api.\n\nSwitching to C++17 worries me as it's fairly recent and may not be\neasily available for all users. std::optional could easily rbe replaced\nwith a custom implementation in utils::optional if needed. I'm tempted\nto leave std::optional in for now, and accept new users of std::optional\nbut no other C++17 APIs. Switching to utils::optional for ColorSpace\nwould cover new users of std::optional so we're not creating any new\nliability.\n\n> Maybe this deserves it's own thread...\n\nDone :-)\n\n> > > I am all in for this change also, personally I would have changed to\n> > > printf for now\n> > > to have one less dependency (also an easy port to C++20 std::format). The\n> > > dependency list is getting large, I noticed a build of mine failing\n> > > recently because\n> > > I didn't have libyaml.\n> > \n> > I've posted a patch that falls back to a subproject in that case. Would\n> > you be able to give it a try ?\n> > \n> > > But std::format and libfmt are quite fast and anything is better than\n> > > streams so +1.\n> > > \n> > > > I've never used (yet) {fmt}, but I've only heard good things about\n> > > > performance, and of course it's headed into the standard, so I also\n> > > > think there is some good merit to be found in this development.","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 2ACC5BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 22 May 2022 10:52:29 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 454E965665;\n\tSun, 22 May 2022 12:52:28 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A024160419\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 22 May 2022 12:52:26 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(static-11-127-145-212.ipcom.comunitel.net [212.145.127.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id AD229563;\n\tSun, 22 May 2022 12:52:24 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653216748;\n\tbh=fWUfZRqVhhL63UoQ6HjJp4MRD6ZiThCDHDRsgPm9S7A=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=18MTi6UrUNUEoZ0d1IXGdwbM4/LRRpfN161hdRVm3/ZLr3RQb26nC1UowH2iQa9U5\n\tT5k8JDgwtfVAmTicp7u5YcQD4TKPr0+NJPumiqE7g+Ptp4gdBhMH8A6HNdcETuh6n0\n\tmY20o/owBMru+4N2k5b7gQmuu4WZtngktdbC9kQyZejcjGHUQP+9MoVCjjxsFANI7O\n\t89ePQZ9HlpA/mXTbBXHVPY0dJbk/JGUU7bOyG/I5OsxKaeTT6H22WfqvhVdtYtWi5f\n\tX/vvf+dphWwBlPjGysjxYXn6X7Irt73srz4NVYdQDDdh2M4HM0rsXIsS9qj3V1XJzS\n\tNk9C2+n3Vwymw==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653216744;\n\tbh=fWUfZRqVhhL63UoQ6HjJp4MRD6ZiThCDHDRsgPm9S7A=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=tx/hGTzStBdspJhniWJeLRXCcao/fddQ2ZWcxsKtfHJIHUmYMXkQQEMmxunPNAmTG\n\tYAaDL3Qex9AW/DHrbsukEy/BOb+17eLCg2RBqXT+L13GUrdsXUjKjR0j7BL1pAsnev\n\ttQPit6A6deebfxU94PhLgBlHQ8NE8rJGG8wxf2eE="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"tx/hGTzS\"; dkim-atps=neutral","Date":"Sun, 22 May 2022 13:52:19 +0300","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>\n\t<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>\n\t<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>\n\t<165321410734.4128907.16683618909815271160@Monstersaurus>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<165321410734.4128907.16683618909815271160@Monstersaurus>","Subject":"[libcamera-devel] Usage of C++17 in the public API (was \"[PATCH]\n\tcam: convert to libfmt\")","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23130,"web_url":"https://patchwork.libcamera.org/comment/23130/","msgid":"<CAOgh=FwRbpZzXbrrFWcnv=gQtVSrmVyon7E7d=oEJWLRMk=ytw@mail.gmail.com>","date":"2022-05-22T20:00:04","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":101,"url":"https://patchwork.libcamera.org/api/people/101/","name":"Eric Curtin","email":"ecurtin@redhat.com"},"content":"On Sun, 22 May 2022 at 10:58, Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi Eric,\n>\n> On Sat, May 21, 2022 at 07:33:03PM +0100, Eric Curtin wrote:\n> > On Tue, 10 May 2022 at 15:21, Laurent Pinchart wrote:\n> > > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote:\n> > > > On 10/05/2022 16:22, Laurent Pinchart wrote:\n> > > > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n> > > > >> This is just a conversation starter, not for merging. I really like\n> > > > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > > > >> is the first library I add to any new C++ project I make.\n> > > > >\n> > > > > You've got it wrong. The first library you should add to any new C++\n> > > > > project you make is libcamera :-)\n> > > > >\n> > > > >> You can find more information about libfmt from:\n> > > > >>\n> > > > >> https://github.com/fmtlib/fmt\n> > > > >> https://fmt.dev/latest/index.html\n> > > > >>\n> > > > >> This patch is just a crude conversion with ugly macros to showcase what\n> > > > >> the formatting code might look like.\n> > > > >>\n> > > > >> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > > > >> than iostreams, but for the size it didn't seem to be true in cam's case\n> > > > >> as the tests below show. However, simple prints did reduce the exe size,\n> > > > >> but the few more complex ones increased the size.\n> > > > >>\n> > > > >> Size tests with gcc 11.2.0-19ubuntu1\n> > > > >>\n> > > > >> - Without libfmt\n> > > > >>\n> > > > >> debug              3523400\n> > > > >> debug lto  3269368\n> > > > >> release            223056\n> > > > >> release lto        172280\n> > > > >>\n> > > > >> - With libfmt\n> > > > >>\n> > > > >> debug              4424256\n> > > > >> debug lto  4143840\n> > > > >> release            303952\n> > > > >> release lto        252640\n> > > > >>\n> > > > >> Above shows that cam's size clearly increases with libfmt. However, the\n> > > > >> increase really comes only from one case, the use of fmt::memory_buffer\n> > > > >> and std::back_inserter.\n> > > > >\n> > > > > Is this because libfmt is a header-only library ? What if it is built as\n> > > > > a shared object, what's the impact on code size in cam ?\n> > > >\n> > > > libfmt supports header-only, but it's not header-only here.\n> > > >\n> > > > >> Converting that code to use fmt::format() and\n> > > > >> naive string append gives:\n> > > > >>\n> > > > >> release with string append 233680\n> > > > >> release lto with string append     186936\n> > > > >\n> > > > > What's the impact of switching to string concatenation on runtime ?\n> > > >\n> > > > What do you mean?\n> > >\n> > > I mean what is the impact on CPU usage of using the \"naive\" string\n> > > append, compared to std::stringstream or fmt::memory_buffer ?\n> > >\n> > > > > Would you consider the option of keeping std::stringstream ?\n> > > >\n> > > > You mean formatting mostly with libfmt, but sometimes with streams? Or\n> > > > using libfmt but appending the formatted string to stringstream?\n> > >\n> > > Mostly using fmt::, but using std::stringstream without\n> > > fmt::memory_buffer when a string needs to be assembled. I'm not saying\n> > > it's necessarily a good idea, just wondering.\n> >\n> > Something I wrote which you can use to format C++ strings in a printf-style\n> >\n> > ```\n> >   #pragma once\n> >\n> >   #include <string.h>\n> >   #include <string>\n> >\n> >   // function that will sprintf to a C++ string starting from std::string::size()\n> >   // so if you want to completely overwrite a string or start at a specific point\n> >   // use std::string::clear() or std::string::resize(). str is a std::string.\n> >   #define STRING_PRINTF(str, ...)                                   \\\n> >     do {                                                            \\\n> >       const int size = snprintf(NULL, 0, __VA_ARGS__);              \\\n> >       const size_t start_of_string = str.size();                    \\\n> >       str.resize(start_of_string + size);                           \\\n> >       snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \\\n> >     } while (0)\n> > ```\n> >\n> > This way you can format C++ strings in a sprintf-style. It would give\n> > you many of the benefits of fmt with just a few lines of code\n> > alternatively. I know we don't only use macros, but it makes sense\n> > here because when the compiler see's the snprintf, it warns against\n> > incorrect format strings when you do it this way.\n>\n> Beside the fact that snprintf() is called twice which may be less\n> efficient (although maybe still more efficient than libfmt), the trouble\n> here is that __VA_ARGS__ will be evaluated twice.\n>\n>         unsigned int index;\n>         std::string str;\n>\n>         index = 0;\n>         STRING_PRINTF(str, \"%u\", index++);\n\nThese simple cases could use:\n\n  std::string str = std::to_string(index++);\n\nIt's unlikely that you are gonna beat std::to_string for a simple\nconversion like this (well, libfmt beats everything in terms of\nexecution speed but it's another dependency, I've done a few\nbenchmarks in the past). STRING_PRINTF would easily convert to\nstd::format in future. The solution is efficient in terms of memory\nbut will do an evaluation twice as you said. The above macro was\nwritten with readability and future compatibility to std::format in\nmind, and is not very different to what folly from Meta does (only\nmuch simpler and compiler friendly), see stringPrintf\n\nhttps://github.com/facebook/folly/blob/main/folly/String.cpp\n\nSTRING_PRINTF suits the case where you have a longer format string and\nyou want to do something a bit more complex, so something like:\n\n        std::stringstream info;\n        info << ts / 1000000000 << \".\"\n             << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000\n             << \" (\" << std::fixed << std::setprecision(2) << fps << \" fps)\";\n\n        for (const auto &[stream, buffer] : buffers) {\n                const FrameMetadata &metadata = buffer->metadata();\n\n                info << \" \" << streamNames_[stream]\n                     << \" seq: \" << std::setw(6) << std::setfill('0')\n<< metadata.sequence\n                     << \" bytesused: \";\n\n                unsigned int nplane = 0;\n                for (const FrameMetadata::Plane &plane : metadata.planes()) {\n                        info << plane.bytesused;\n                        if (++nplane < metadata.planes().size())\n                                info << \"/\";\n                }\n        }\n\n        if (sink_) {\n                if (!sink_->processRequest(request))\n                        requeue = false;\n        }\n\n        std::cout << info.str() << std::endl;\n\nwould become:\n\n        std::string info\n        STRING_PRINTF(info, \"%.6f (%.2f fps)\", ts / 1000000000.0, fps);\n\n        for (const auto &[stream, buffer] : buffers) {\n                const FrameMetadata &metadata = buffer->metadata();\n\n                STRING_PRINTF(info,\n                             \" %s seq: %06d bytesused: \",\nstreamNames_[buf.first].c_str(),\n                             metadata.sequence);\n\n                unsigned int nplane = 0;\n                for (const FrameMetadata::Plane &plane : metadata.planes()) {\n                        frame_str += std::to_string(plane.bytesused);\n                        if (++nplane < metadata.planes().size())\n                                info += \"/\";\n                }\n        }\n\n        if (sink_) {\n                if (!sink_->processRequest(request))\n                        requeue = false;\n        }\n\n        printf(\"%s\\n\", info.c_str());\n\n>         /* Now index is equal to 2 */\n>\n> > Just an option...\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 B6732BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 22 May 2022 20:00:26 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D0A0C65666;\n\tSun, 22 May 2022 22:00:25 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2096B6041B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 22 May 2022 22:00:24 +0200 (CEST)","from mail-qk1-f198.google.com (mail-qk1-f198.google.com\n\t[209.85.222.198]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n\tus-mta-561-UY2vw8ulPiO0NnjyGFY4lQ-1; Sun, 22 May 2022 16:00:21 -0400","by mail-qk1-f198.google.com with SMTP id\n\to13-20020a05620a0d4d00b0069f47054e58so10183867qkl.13\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 22 May 2022 13:00:21 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653249625;\n\tbh=zj8H/mGjbZ43BrJaGsHwfn8JXySWY2G2vLUS2a4qVf4=;\n\th=References:In-Reply-To:Date:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=03t8ZPxyQf4rNtxRF2oXxzBDHIZepMKqgfMk8KSfiqP82p+9i6nvYeiJ9bi6m/Apg\n\tVnvgTRsigqNkNZ8Vq0GCR7qex5szXPPNTzWxeC8OZo7rC6YsgDnZ4D+jUwW2S+Aozk\n\tSFVHyeQNw71ZHVhQ2LYFnPe9usFruKyckVDR+FuAuNqcDH//iLw2xRvFNM74z1SiYu\n\tEHOOW/uzBepXXZ+OO40xdPJKPWgNVar+YbdO6zoj7DVCCc6RhU7SrTBtdm/JPZ/jMl\n\tQ68Snm+HEpX5rnMXepRwhXTATD8GPm6DppVxpkyMd8slDvixIbr8F4PKhPd7K1auWB\n\tF9AdNTTQvVn7g==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1653249622;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=A8E/6uwAqCs2kDooI5KZ8ChJZfLaRR3zV8+odI+8L0Q=;\n\tb=MnN2w52I9JKiCyd/zFokYrgInL6qwOlJ5DW/XmHP0vczXMqKGcIwvAMD47Jo60wNMK7N2+\n\tgakLHDaIV6u+mrmrzFf25LsfsqDQWeU/gPVWjV9kBSx+0471bFIavI7UwuTel8wUTdk5kw\n\t2CNRTl1Mio29naq0slGKCI5+Tg70S8E="],"Authentication-Results":["lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=redhat.com\n\theader.i=@redhat.com header.b=\"MnN2w52I\"; \n\tdkim-atps=neutral","relay.mimecast.com;\n\tauth=pass smtp.auth=CUSA124A263 smtp.mailfrom=ecurtin@redhat.com"],"X-MC-Unique":"UY2vw8ulPiO0NnjyGFY4lQ-1","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=A8E/6uwAqCs2kDooI5KZ8ChJZfLaRR3zV8+odI+8L0Q=;\n\tb=aK1nn8xstEtU8LXfuHvfmKmXlcZYo2D6BFcv1955xpwzhLxUtXnSpUz5eC12aGKtFA\n\twfLK/jkFLSs/zSlj9g3npowXktETBJ9pTqVSKnwU6B8uDNOKI/5pKIylDelOU3OS+Tcv\n\tMYfmq7IFRBVAoqneYBqKsi7BTDLZDeKR8cWVG8hk/P1SLT2Mvuga528kJ0piFRYaYjHV\n\tUvCEXt2f3qZmEY/pOVo3+Omr0Wg3RnoadQ0gxrKyCcCO28M/JhUwppM33WEKkRORPjks\n\tgqt8z0IgTbtZOdrlkpUuCQpShs5mz3x61/CqZVgiy+SuEl/SM0Vd55iWjaAy0eK3ekOl\n\tXycg==","X-Gm-Message-State":"AOAM5305hvx6dz5aGZM/5gyl5raWdjERaso8J3L8KvmVC7uXLO9eefnY\n\tUveb/ST/TCqnroTg2jGtxfAV0LfrvTYr5uCsqn+qBRw0gEN3tBopuvXhi4eImshpQLCCSffw3CI\n\txqJwcbDRTi/nZ5r2QeGk0xtJcw4+0y3N1MiNgDDGLJdFBcziniQ==","X-Received":["by 2002:a05:622a:15c8:b0:2f3:d7ae:bae6 with SMTP id\n\td8-20020a05622a15c800b002f3d7aebae6mr14419569qty.106.1653249620727; \n\tSun, 22 May 2022 13:00:20 -0700 (PDT)","by 2002:a05:622a:15c8:b0:2f3:d7ae:bae6 with SMTP id\n\td8-20020a05622a15c800b002f3d7aebae6mr14419558qty.106.1653249620297;\n\tSun, 22 May 2022 13:00:20 -0700 (PDT)"],"X-Google-Smtp-Source":"ABdhPJziqXsUxLvAyZPmRPUHdgorKicXQKRhzK8rnYLPeUQhC1ZbirLo2LLNnGMsyFK5Zx6el4aIIH7sfjDccQXwGUU=","MIME-Version":"1.0","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>\n\t<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>\n\t<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>\n\t<CAOgh=FzT-AuDvZMKkdXb398xJN7egZevgjXWyh2R=Pqx0gBKMg@mail.gmail.com>\n\t<YooJVH/6UU9HmOje@pendragon.ideasonboard.com>","In-Reply-To":"<YooJVH/6UU9HmOje@pendragon.ideasonboard.com>","Date":"Sun, 22 May 2022 21:00:04 +0100","Message-ID":"<CAOgh=FwRbpZzXbrrFWcnv=gQtVSrmVyon7E7d=oEJWLRMk=ytw@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","X-Mimecast-Spam-Score":"0","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Eric Curtin via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Eric Curtin <ecurtin@redhat.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23134,"web_url":"https://patchwork.libcamera.org/comment/23134/","msgid":"<YotEjxrf6ZirT+I7@pendragon.ideasonboard.com>","date":"2022-05-23T08:23:43","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Eric,\n\nOn Sun, May 22, 2022 at 09:00:04PM +0100, Eric Curtin wrote:\n> On Sun, 22 May 2022 at 10:58, Laurent Pinchart wrote:\n> > On Sat, May 21, 2022 at 07:33:03PM +0100, Eric Curtin wrote:\n> > > On Tue, 10 May 2022 at 15:21, Laurent Pinchart wrote:\n> > > > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote:\n> > > > > On 10/05/2022 16:22, Laurent Pinchart wrote:\n> > > > > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n> > > > > >> This is just a conversation starter, not for merging. I really like\n> > > > > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > > > > >> is the first library I add to any new C++ project I make.\n> > > > > >\n> > > > > > You've got it wrong. The first library you should add to any new C++\n> > > > > > project you make is libcamera :-)\n> > > > > >\n> > > > > >> You can find more information about libfmt from:\n> > > > > >>\n> > > > > >> https://github.com/fmtlib/fmt\n> > > > > >> https://fmt.dev/latest/index.html\n> > > > > >>\n> > > > > >> This patch is just a crude conversion with ugly macros to showcase what\n> > > > > >> the formatting code might look like.\n> > > > > >>\n> > > > > >> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > > > > >> than iostreams, but for the size it didn't seem to be true in cam's case\n> > > > > >> as the tests below show. However, simple prints did reduce the exe size,\n> > > > > >> but the few more complex ones increased the size.\n> > > > > >>\n> > > > > >> Size tests with gcc 11.2.0-19ubuntu1\n> > > > > >>\n> > > > > >> - Without libfmt\n> > > > > >>\n> > > > > >> debug              3523400\n> > > > > >> debug lto  3269368\n> > > > > >> release            223056\n> > > > > >> release lto        172280\n> > > > > >>\n> > > > > >> - With libfmt\n> > > > > >>\n> > > > > >> debug              4424256\n> > > > > >> debug lto  4143840\n> > > > > >> release            303952\n> > > > > >> release lto        252640\n> > > > > >>\n> > > > > >> Above shows that cam's size clearly increases with libfmt. However, the\n> > > > > >> increase really comes only from one case, the use of fmt::memory_buffer\n> > > > > >> and std::back_inserter.\n> > > > > >\n> > > > > > Is this because libfmt is a header-only library ? What if it is built as\n> > > > > > a shared object, what's the impact on code size in cam ?\n> > > > >\n> > > > > libfmt supports header-only, but it's not header-only here.\n> > > > >\n> > > > > >> Converting that code to use fmt::format() and\n> > > > > >> naive string append gives:\n> > > > > >>\n> > > > > >> release with string append 233680\n> > > > > >> release lto with string append     186936\n> > > > > >\n> > > > > > What's the impact of switching to string concatenation on runtime ?\n> > > > >\n> > > > > What do you mean?\n> > > >\n> > > > I mean what is the impact on CPU usage of using the \"naive\" string\n> > > > append, compared to std::stringstream or fmt::memory_buffer ?\n> > > >\n> > > > > > Would you consider the option of keeping std::stringstream ?\n> > > > >\n> > > > > You mean formatting mostly with libfmt, but sometimes with streams? Or\n> > > > > using libfmt but appending the formatted string to stringstream?\n> > > >\n> > > > Mostly using fmt::, but using std::stringstream without\n> > > > fmt::memory_buffer when a string needs to be assembled. I'm not saying\n> > > > it's necessarily a good idea, just wondering.\n> > >\n> > > Something I wrote which you can use to format C++ strings in a printf-style\n> > >\n> > > ```\n> > >   #pragma once\n> > >\n> > >   #include <string.h>\n> > >   #include <string>\n> > >\n> > >   // function that will sprintf to a C++ string starting from std::string::size()\n> > >   // so if you want to completely overwrite a string or start at a specific point\n> > >   // use std::string::clear() or std::string::resize(). str is a std::string.\n> > >   #define STRING_PRINTF(str, ...)                                   \\\n> > >     do {                                                            \\\n> > >       const int size = snprintf(NULL, 0, __VA_ARGS__);              \\\n> > >       const size_t start_of_string = str.size();                    \\\n> > >       str.resize(start_of_string + size);                           \\\n> > >       snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \\\n> > >     } while (0)\n> > > ```\n> > >\n> > > This way you can format C++ strings in a sprintf-style. It would give\n> > > you many of the benefits of fmt with just a few lines of code\n> > > alternatively. I know we don't only use macros, but it makes sense\n> > > here because when the compiler see's the snprintf, it warns against\n> > > incorrect format strings when you do it this way.\n> >\n> > Beside the fact that snprintf() is called twice which may be less\n> > efficient (although maybe still more efficient than libfmt), the trouble\n> > here is that __VA_ARGS__ will be evaluated twice.\n> >\n> >         unsigned int index;\n> >         std::string str;\n> >\n> >         index = 0;\n> >         STRING_PRINTF(str, \"%u\", index++);\n> \n> These simple cases could use:\n> \n>   std::string str = std::to_string(index++);\n\nOf course. It was just an example of potential issues with multiple\nevaluation of macro arguments. It should be possible to turn the macro\ninto a function instead, but I think we'll then end up reinventing the\nwheel.\n\n> It's unlikely that you are gonna beat std::to_string for a simple\n> conversion like this (well, libfmt beats everything in terms of\n> execution speed but it's another dependency, I've done a few\n> benchmarks in the past). STRING_PRINTF would easily convert to\n> std::format in future. The solution is efficient in terms of memory\n> but will do an evaluation twice as you said. The above macro was\n> written with readability and future compatibility to std::format in\n> mind, and is not very different to what folly from Meta does (only\n> much simpler and compiler friendly), see stringPrintf\n> \n> https://github.com/facebook/folly/blob/main/folly/String.cpp\n> \n> STRING_PRINTF suits the case where you have a longer format string and\n> you want to do something a bit more complex, so something like:\n> \n>         std::stringstream info;\n>         info << ts / 1000000000 << \".\"\n>              << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000\n>              << \" (\" << std::fixed << std::setprecision(2) << fps << \" fps)\";\n> \n>         for (const auto &[stream, buffer] : buffers) {\n>                 const FrameMetadata &metadata = buffer->metadata();\n> \n>                 info << \" \" << streamNames_[stream]\n>                      << \" seq: \" << std::setw(6) << std::setfill('0')\n> << metadata.sequence\n>                      << \" bytesused: \";\n> \n>                 unsigned int nplane = 0;\n>                 for (const FrameMetadata::Plane &plane : metadata.planes()) {\n>                         info << plane.bytesused;\n>                         if (++nplane < metadata.planes().size())\n>                                 info << \"/\";\n>                 }\n>         }\n> \n>         if (sink_) {\n>                 if (!sink_->processRequest(request))\n>                         requeue = false;\n>         }\n> \n>         std::cout << info.str() << std::endl;\n> \n> would become:\n> \n>         std::string info\n>         STRING_PRINTF(info, \"%.6f (%.2f fps)\", ts / 1000000000.0, fps);\n> \n>         for (const auto &[stream, buffer] : buffers) {\n>                 const FrameMetadata &metadata = buffer->metadata();\n> \n>                 STRING_PRINTF(info,\n>                              \" %s seq: %06d bytesused: \", streamNames_[buf.first].c_str(),\n>                              metadata.sequence);\n> \n>                 unsigned int nplane = 0;\n>                 for (const FrameMetadata::Plane &plane : metadata.planes()) {\n>                         frame_str += std::to_string(plane.bytesused);\n>                         if (++nplane < metadata.planes().size())\n>                                 info += \"/\";\n>                 }\n>         }\n> \n>         if (sink_) {\n>                 if (!sink_->processRequest(request))\n>                         requeue = false;\n>         }\n> \n>         printf(\"%s\\n\", info.c_str());\n> \n> >         /* Now index is equal to 2 */\n> >\n> > > Just an option...","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 A0739BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 23 May 2022 08:23:51 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 01BEF65664;\n\tMon, 23 May 2022 10:23:51 +0200 (CEST)","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 286006565E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 23 May 2022 10:23:50 +0200 (CEST)","from pendragon.ideasonboard.com (ip-109-40-241-50.web.vodafone.de\n\t[109.40.241.50])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 691DF45F;\n\tMon, 23 May 2022 10:23:49 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653294231;\n\tbh=Y+/SAW9N3nMNCzS68VXWkArzAWU4A5KinS7aL1C/1Mg=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=q0AnKFRnfxfbWtYK/iQQ4GuF2gyQcgAt6/KoUEcF+X2o0FmJUt9fu5RmGOmLpRb6U\n\tMwdrdO+o07TwIbZjRWaeMbt+D823cdJTa7Cw06nuNJ+Dht+2Yq+vm1HSSSm7zfbkk6\n\trpRags6J7ikSG/dyCQ7czHVRgLPzSMvJcb7pFj9EbiE5v+Fok8fCs84omSrh5nupVG\n\t/K/aTlKuSgdOO4Hz42kAdAVRgie1zGpHj6+YeANspnqDcLktVnI7E5UjVjm8JaLaET\n\tK8/XWVyAkbJC9gtR2gtgFelkak4wccUOcEj1siStZ623uyrE3BpIIU6BtKAtYKE9i+\n\tDiCbTE31Rn9Kw==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653294229;\n\tbh=Y+/SAW9N3nMNCzS68VXWkArzAWU4A5KinS7aL1C/1Mg=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=rvKT2wpJ3L1lotnf4P2FI1rpC5/fgTI9Ay4PGFieTyzrIsPLQQ7Gu4Yidz7eK3fKN\n\tq1k0o+Jb9xRxyxqEu8gBU3fYO9BE/P5G9q3KcmotbY5oPGVnrOIcR6dG2f+ZSI9XHr\n\thH2KaBU9BujLOJxFKVh2YPjAJaiAEe3yEpQpFMzY="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"rvKT2wpJ\"; dkim-atps=neutral","Date":"Mon, 23 May 2022 11:23:43 +0300","To":"Eric Curtin <ecurtin@redhat.com>","Message-ID":"<YotEjxrf6ZirT+I7@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>\n\t<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>\n\t<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>\n\t<CAOgh=FzT-AuDvZMKkdXb398xJN7egZevgjXWyh2R=Pqx0gBKMg@mail.gmail.com>\n\t<YooJVH/6UU9HmOje@pendragon.ideasonboard.com>\n\t<CAOgh=FwRbpZzXbrrFWcnv=gQtVSrmVyon7E7d=oEJWLRMk=ytw@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<CAOgh=FwRbpZzXbrrFWcnv=gQtVSrmVyon7E7d=oEJWLRMk=ytw@mail.gmail.com>","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23136,"web_url":"https://patchwork.libcamera.org/comment/23136/","msgid":"<CAOgh=Fzidi79eptP0r2AFREFH4O=Pg7XQ4guL1g31qUavn-=pQ@mail.gmail.com>","date":"2022-05-23T09:40:13","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":101,"url":"https://patchwork.libcamera.org/api/people/101/","name":"Eric Curtin","email":"ecurtin@redhat.com"},"content":"On Mon, 23 May 2022 at 09:23, Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Hi Eric,\n>\n> On Sun, May 22, 2022 at 09:00:04PM +0100, Eric Curtin wrote:\n> > On Sun, 22 May 2022 at 10:58, Laurent Pinchart wrote:\n> > > On Sat, May 21, 2022 at 07:33:03PM +0100, Eric Curtin wrote:\n> > > > On Tue, 10 May 2022 at 15:21, Laurent Pinchart wrote:\n> > > > > On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote:\n> > > > > > On 10/05/2022 16:22, Laurent Pinchart wrote:\n> > > > > > > On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n> > > > > > >> This is just a conversation starter, not for merging. I really like\n> > > > > > >> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n> > > > > > >> is the first library I add to any new C++ project I make.\n> > > > > > >\n> > > > > > > You've got it wrong. The first library you should add to any new C++\n> > > > > > > project you make is libcamera :-)\n> > > > > > >\n> > > > > > >> You can find more information about libfmt from:\n> > > > > > >>\n> > > > > > >> https://github.com/fmtlib/fmt\n> > > > > > >> https://fmt.dev/latest/index.html\n> > > > > > >>\n> > > > > > >> This patch is just a crude conversion with ugly macros to showcase what\n> > > > > > >> the formatting code might look like.\n> > > > > > >>\n> > > > > > >> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n> > > > > > >> than iostreams, but for the size it didn't seem to be true in cam's case\n> > > > > > >> as the tests below show. However, simple prints did reduce the exe size,\n> > > > > > >> but the few more complex ones increased the size.\n> > > > > > >>\n> > > > > > >> Size tests with gcc 11.2.0-19ubuntu1\n> > > > > > >>\n> > > > > > >> - Without libfmt\n> > > > > > >>\n> > > > > > >> debug              3523400\n> > > > > > >> debug lto  3269368\n> > > > > > >> release            223056\n> > > > > > >> release lto        172280\n> > > > > > >>\n> > > > > > >> - With libfmt\n> > > > > > >>\n> > > > > > >> debug              4424256\n> > > > > > >> debug lto  4143840\n> > > > > > >> release            303952\n> > > > > > >> release lto        252640\n> > > > > > >>\n> > > > > > >> Above shows that cam's size clearly increases with libfmt. However, the\n> > > > > > >> increase really comes only from one case, the use of fmt::memory_buffer\n> > > > > > >> and std::back_inserter.\n> > > > > > >\n> > > > > > > Is this because libfmt is a header-only library ? What if it is built as\n> > > > > > > a shared object, what's the impact on code size in cam ?\n> > > > > >\n> > > > > > libfmt supports header-only, but it's not header-only here.\n> > > > > >\n> > > > > > >> Converting that code to use fmt::format() and\n> > > > > > >> naive string append gives:\n> > > > > > >>\n> > > > > > >> release with string append 233680\n> > > > > > >> release lto with string append     186936\n> > > > > > >\n> > > > > > > What's the impact of switching to string concatenation on runtime ?\n> > > > > >\n> > > > > > What do you mean?\n> > > > >\n> > > > > I mean what is the impact on CPU usage of using the \"naive\" string\n> > > > > append, compared to std::stringstream or fmt::memory_buffer ?\n> > > > >\n> > > > > > > Would you consider the option of keeping std::stringstream ?\n> > > > > >\n> > > > > > You mean formatting mostly with libfmt, but sometimes with streams? Or\n> > > > > > using libfmt but appending the formatted string to stringstream?\n> > > > >\n> > > > > Mostly using fmt::, but using std::stringstream without\n> > > > > fmt::memory_buffer when a string needs to be assembled. I'm not saying\n> > > > > it's necessarily a good idea, just wondering.\n> > > >\n> > > > Something I wrote which you can use to format C++ strings in a printf-style\n> > > >\n> > > > ```\n> > > >   #pragma once\n> > > >\n> > > >   #include <string.h>\n> > > >   #include <string>\n> > > >\n> > > >   // function that will sprintf to a C++ string starting from std::string::size()\n> > > >   // so if you want to completely overwrite a string or start at a specific point\n> > > >   // use std::string::clear() or std::string::resize(). str is a std::string.\n> > > >   #define STRING_PRINTF(str, ...)                                   \\\n> > > >     do {                                                            \\\n> > > >       const int size = snprintf(NULL, 0, __VA_ARGS__);              \\\n> > > >       const size_t start_of_string = str.size();                    \\\n> > > >       str.resize(start_of_string + size);                           \\\n> > > >       snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \\\n> > > >     } while (0)\n> > > > ```\n> > > >\n> > > > This way you can format C++ strings in a sprintf-style. It would give\n> > > > you many of the benefits of fmt with just a few lines of code\n> > > > alternatively. I know we don't only use macros, but it makes sense\n> > > > here because when the compiler see's the snprintf, it warns against\n> > > > incorrect format strings when you do it this way.\n> > >\n> > > Beside the fact that snprintf() is called twice which may be less\n> > > efficient (although maybe still more efficient than libfmt), the trouble\n> > > here is that __VA_ARGS__ will be evaluated twice.\n> > >\n> > >         unsigned int index;\n> > >         std::string str;\n> > >\n> > >         index = 0;\n> > >         STRING_PRINTF(str, \"%u\", index++);\n> >\n> > These simple cases could use:\n> >\n> >   std::string str = std::to_string(index++);\n>\n> Of course. It was just an example of potential issues with multiple\n> evaluation of macro arguments. It should be possible to turn the macro\n> into a function instead, but I think we'll then end up reinventing the\n> wheel.\n\nYeah I get that, I'm not exactly hell bent on changing this, just\ncuriosity at this point (although admittedly I prefer printf or libfmt\nstyle, and it's generally faster).\n\nI did a crude benchmark here, just to solve curiosity:\n\nhttps://github.com/ericcurtin/staging/blob/master/string_format_bench.cpp\n\n$ g++ -O3 string_format_bench.cpp; ./a.out > /dev/null\nfolly: 3.447348 seconds\nfolly2: 3.343155 seconds\nstring_printf: 7.059757 seconds\nstring_printf2: 3.468643 seconds\nto_string: 4.890018 seconds\nostringstream: 5.817828 seconds\n\nstring_printf2 takes some influence from folly and the first time it\nparses, it optimistically guesses the output string could be less than\n128 characters, which it probably is in many cases.\n\nI actually wrote folly2 as a further optimization and opened a pull\nrequest in Meta's folly, they missed a trick there by not writing\ndirectly into the string, they do an extra unnecessary copy.\n\nSTRING_PRINTF2 is my favorite, but each to their own :)\n\n>\n> > It's unlikely that you are gonna beat std::to_string for a simple\n> > conversion like this (well, libfmt beats everything in terms of\n> > execution speed but it's another dependency, I've done a few\n> > benchmarks in the past). STRING_PRINTF would easily convert to\n> > std::format in future. The solution is efficient in terms of memory\n> > but will do an evaluation twice as you said. The above macro was\n> > written with readability and future compatibility to std::format in\n> > mind, and is not very different to what folly from Meta does (only\n> > much simpler and compiler friendly), see stringPrintf\n> >\n> > https://github.com/facebook/folly/blob/main/folly/String.cpp\n> >\n> > STRING_PRINTF suits the case where you have a longer format string and\n> > you want to do something a bit more complex, so something like:\n> >\n> >         std::stringstream info;\n> >         info << ts / 1000000000 << \".\"\n> >              << std::setw(6) << std::setfill('0') << ts / 1000 % 1000000\n> >              << \" (\" << std::fixed << std::setprecision(2) << fps << \" fps)\";\n> >\n> >         for (const auto &[stream, buffer] : buffers) {\n> >                 const FrameMetadata &metadata = buffer->metadata();\n> >\n> >                 info << \" \" << streamNames_[stream]\n> >                      << \" seq: \" << std::setw(6) << std::setfill('0')\n> > << metadata.sequence\n> >                      << \" bytesused: \";\n> >\n> >                 unsigned int nplane = 0;\n> >                 for (const FrameMetadata::Plane &plane : metadata.planes()) {\n> >                         info << plane.bytesused;\n> >                         if (++nplane < metadata.planes().size())\n> >                                 info << \"/\";\n> >                 }\n> >         }\n> >\n> >         if (sink_) {\n> >                 if (!sink_->processRequest(request))\n> >                         requeue = false;\n> >         }\n> >\n> >         std::cout << info.str() << std::endl;\n> >\n> > would become:\n> >\n> >         std::string info\n> >         STRING_PRINTF(info, \"%.6f (%.2f fps)\", ts / 1000000000.0, fps);\n> >\n> >         for (const auto &[stream, buffer] : buffers) {\n> >                 const FrameMetadata &metadata = buffer->metadata();\n> >\n> >                 STRING_PRINTF(info,\n> >                              \" %s seq: %06d bytesused: \", streamNames_[buf.first].c_str(),\n> >                              metadata.sequence);\n> >\n> >                 unsigned int nplane = 0;\n> >                 for (const FrameMetadata::Plane &plane : metadata.planes()) {\n> >                         frame_str += std::to_string(plane.bytesused);\n> >                         if (++nplane < metadata.planes().size())\n> >                                 info += \"/\";\n> >                 }\n> >         }\n> >\n> >         if (sink_) {\n> >                 if (!sink_->processRequest(request))\n> >                         requeue = false;\n> >         }\n> >\n> >         printf(\"%s\\n\", info.c_str());\n> >\n> > >         /* Now index is equal to 2 */\n> > >\n> > > > Just an option...\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 12A9ABD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 23 May 2022 09:40:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 4EB0C65664;\n\tMon, 23 May 2022 11:40:34 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 932476565E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 23 May 2022 11:40:32 +0200 (CEST)","from mail-qv1-f71.google.com (mail-qv1-f71.google.com\n\t[209.85.219.71]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n\tus-mta-194-GsKYdDHiPnSAwCDgH52-IA-1; Mon, 23 May 2022 05:40:30 -0400","by mail-qv1-f71.google.com with SMTP id\n\tr13-20020a056214068d00b00461d27bf0fdso10592240qvz.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 23 May 2022 02:40:30 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653298834;\n\tbh=QNChpQ9uUz8r4U027Km49yN2r4pbFSlZ9zdcPD9qr8o=;\n\th=References:In-Reply-To:Date:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=v4tk2g/KK3/seBOdXidsNEUa/n6QA2KX58O3X5SkTLefGy98vxTUZSrLOfjOx9N6h\n\t1gYQkDWyTwJ/FZRG4hfBiSVagOOul1+Hweem/Pvy535NqtLLIMXYIwAkM/USt3R/eJ\n\t7hWU2VIJD08NnlAHg/D2sK0+Lie4Bd265rSm45hRSAjqqei5pM2hUn2B/Lh+r3hG6l\n\t/gHd6eVE+Mk1QsEWKldQ3zPe9Tf1nhDalU0UjkMmUQ+JYVmpTfhEIh0ZJTIWk8pmEN\n\tuADQrWWHFYfrZZx0ygCJyavvVmwW3lJiYSBFK6eLSDpmNLVTTuMeyQLxfhNBUtnKL3\n\tGUo5ACLt2ukLg==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1653298831;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=Q5IodZUXhtI3TCRsKVwBdXZaIDO/WzQQdDXLavvrPEc=;\n\tb=BlxQJ+33W3RZNK5L6GkEqcoevAAqLtjhBSjs3yeb1FuzK6i8LUJN0zL/mWdeDTYge0t0p9\n\tZmE6SFDchUhMwsw3HYy1pCP7DS5uifm1xu5yTAkWUnVFIJMbYJR31o6pBDFeGjJsUwc7Ly\n\tUccpVANPstYeolFCV5W8dMmWTx8LPhI="],"Authentication-Results":["lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=redhat.com\n\theader.i=@redhat.com header.b=\"BlxQJ+33\"; \n\tdkim-atps=neutral","relay.mimecast.com;\n\tauth=pass smtp.auth=CUSA124A263 smtp.mailfrom=ecurtin@redhat.com"],"X-MC-Unique":"GsKYdDHiPnSAwCDgH52-IA-1","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=Q5IodZUXhtI3TCRsKVwBdXZaIDO/WzQQdDXLavvrPEc=;\n\tb=hr/VrX81Nyp1Ke0MpY4YzR0bEA1R72ArjgV1RBcwrhhVtEHjLjEe4cD7J/zhAhxAur\n\tW+IEpwQLqckvCGig1EfJycK/PK5ysanDRNHYy2sLcAat04qU+Qo9U81amvbfw3GtNYu2\n\tI+Bcd272dB5/Vt3ZXJtNoHk8N9yl/jTnI4Jf66LXUNwBjZ72O3EWuMWrPb/bUeCJuiHl\n\tGNgeBmQ6OtgLuVAQQiacJbwD2tAw+ry8R9UjU+48MQBBdTxUSz2P2IfE0/vjycZt/Cqh\n\tUZ4ruKemVClaTTfs788hy1UD3RMEj5BTpMy+w7P1lCxd1Ho+AhEeXxkw6gBErciRWJbv\n\tNURg==","X-Gm-Message-State":"AOAM531QPGTw4zxVdR/Z5+He6g7/ptm42Mu5pJCgiylrq/E9BAcrDz/+\n\tRuBTX3GBTz9vW5q9dgPwlWQFBYV1nQGhnssNA/URBArf98DDVCi0D5zVQrzuUWKRfbY6UC6HBgE\n\tbFlJgfDqvPKg1WRIywEXLF94a8m2ZUHuWYw99TSBZB5eXtBxqkQ==","X-Received":["by 2002:ac8:7d81:0:b0:2f3:ca7a:653b with SMTP id\n\tc1-20020ac87d81000000b002f3ca7a653bmr15525497qtd.638.1653298829707; \n\tMon, 23 May 2022 02:40:29 -0700 (PDT)","by 2002:ac8:7d81:0:b0:2f3:ca7a:653b with SMTP id\n\tc1-20020ac87d81000000b002f3ca7a653bmr15525487qtd.638.1653298829423;\n\tMon, 23 May 2022 02:40:29 -0700 (PDT)"],"X-Google-Smtp-Source":"ABdhPJz6K7LaUSZptclYCaWg3O7dYFvOq6Wr3k5TjGTwWTi9lju/CJtZTeOtFiRbVSTdyL4Itt1sFcHsT83Ud604b5M=","MIME-Version":"1.0","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>\n\t<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>\n\t<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>\n\t<CAOgh=FzT-AuDvZMKkdXb398xJN7egZevgjXWyh2R=Pqx0gBKMg@mail.gmail.com>\n\t<YooJVH/6UU9HmOje@pendragon.ideasonboard.com>\n\t<CAOgh=FwRbpZzXbrrFWcnv=gQtVSrmVyon7E7d=oEJWLRMk=ytw@mail.gmail.com>\n\t<YotEjxrf6ZirT+I7@pendragon.ideasonboard.com>","In-Reply-To":"<YotEjxrf6ZirT+I7@pendragon.ideasonboard.com>","Date":"Mon, 23 May 2022 10:40:13 +0100","Message-ID":"<CAOgh=Fzidi79eptP0r2AFREFH4O=Pg7XQ4guL1g31qUavn-=pQ@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","X-Mimecast-Spam-Score":"0","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Eric Curtin via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Eric Curtin <ecurtin@redhat.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23139,"web_url":"https://patchwork.libcamera.org/comment/23139/","msgid":"<2b6ebd64-e194-bb9f-599a-a5ff4302fb40@ideasonboard.com>","date":"2022-05-23T11:19:45","subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","submitter":{"id":109,"url":"https://patchwork.libcamera.org/api/people/109/","name":"Tomi Valkeinen","email":"tomi.valkeinen@ideasonboard.com"},"content":"On 10/05/2022 17:20, Laurent Pinchart wrote:\n> Hi Tomi,\n> \n> On Tue, May 10, 2022 at 05:05:05PM +0300, Tomi Valkeinen wrote:\n>> On 10/05/2022 16:22, Laurent Pinchart wrote:\n>>> On Tue, May 10, 2022 at 10:16:17AM +0300, Tomi Valkeinen wrote:\n>>>> This is just a conversation starter, not for merging. I really like\n>>>> libfmt, and I really hate the C++ stream-based string formatting. libfmt\n>>>> is the first library I add to any new C++ project I make.\n>>>\n>>> You've got it wrong. The first library you should add to any new C++\n>>> project you make is libcamera :-)\n>>>\n>>>> You can find more information about libfmt from:\n>>>>\n>>>> https://github.com/fmtlib/fmt\n>>>> https://fmt.dev/latest/index.html\n>>>>\n>>>> This patch is just a crude conversion with ugly macros to showcase what\n>>>> the formatting code might look like.\n>>>>\n>>>> libfmt pages suggest that libfmt would be faster and smaller (exe size)\n>>>> than iostreams, but for the size it didn't seem to be true in cam's case\n>>>> as the tests below show. However, simple prints did reduce the exe size,\n>>>> but the few more complex ones increased the size.\n>>>>\n>>>> Size tests with gcc 11.2.0-19ubuntu1\n>>>>\n>>>> - Without libfmt\n>>>>\n>>>> debug\t\t3523400\n>>>> debug lto\t3269368\n>>>> release\t\t223056\n>>>> release lto\t172280\n>>>>\n>>>> - With libfmt\n>>>>\n>>>> debug\t\t4424256\n>>>> debug lto\t4143840\n>>>> release\t\t303952\n>>>> release lto\t252640\n>>>>\n>>>> Above shows that cam's size clearly increases with libfmt. However, the\n>>>> increase really comes only from one case, the use of fmt::memory_buffer\n>>>> and std::back_inserter.\n>>>\n>>> Is this because libfmt is a header-only library ? What if it is built as\n>>> a shared object, what's the impact on code size in cam ?\n>>\n>> libfmt supports header-only, but it's not header-only here.\n>>\n>>>> Converting that code to use fmt::format() and\n>>>> naive string append gives:\n>>>>\n>>>> release with string append\t233680\n>>>> release lto with string append\t186936\n>>>\n>>> What's the impact of switching to string concatenation on runtime ?\n>>\n>> What do you mean?\n> \n> I mean what is the impact on CPU usage of using the \"naive\" string\n> append, compared to std::stringstream or fmt::memory_buffer ?\n> \n>>> Would you consider the option of keeping std::stringstream ?\n>>\n>> You mean formatting mostly with libfmt, but sometimes with streams? Or\n>> using libfmt but appending the formatted string to stringstream?\n> \n> Mostly using fmt::, but using std::stringstream without\n> fmt::memory_buffer when a string needs to be assembled. I'm not saying\n> it's necessarily a good idea, just wondering.\n\nI did test using fmt::format() and assembling the result to \nstd::stringstream, and that does keep the executable size down.\n\nSo somehow fmt::format_to() seems to pull in a lot of code. I got a \ncomment on libfmt gitter: \"Normally format and format_to are compiled to \nthe same underlying calls where the iterator is type erased.\". I'm \ncurrently inclined to believe it's a bug.\n\n  Tomi","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 4AFE2BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 23 May 2022 11:19:50 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 861F165661;\n\tMon, 23 May 2022 13:19:49 +0200 (CEST)","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 47D0860416\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 23 May 2022 13:19:48 +0200 (CEST)","from [192.168.1.111] (91-156-85-209.elisa-laajakaista.fi\n\t[91.156.85.209])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 89CC04A8;\n\tMon, 23 May 2022 13:19:47 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653304789;\n\tbh=iWpx7y15yPQQ67N6o4f481OScI0jsv0CVxurfWlhUOg=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=zqFTYDyxn95Xh3rrH63NIgDy3bAKggxx/COYmwggeOPeKqv+iUHvlTsjh27HtK5il\n\t4PLf3kFw1ji/GvznsVPozKCOhhD1r8I5bun0i4LS46JohRXKbGpUAVINAI95Z6h/QL\n\txjUZodzV5t+Q/lxw3hS4Rn2ktEdSTbhg8GL/fgctzAyCP6eifuqg5+/AWDiNYcramF\n\tp8MkzEffcTRxaofn0M93VP6QKWYsyXzv0p6jx4JQpLT2ceIHR5tuC0oZXivx72n/j3\n\tAlzn/faehfUJvH+CWAV6B5AsIST16wp2qu5EsfScCnsHXdcbhqlHUz2UrVurF0Aye3\n\tBDPY47o+0XdcA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653304787;\n\tbh=iWpx7y15yPQQ67N6o4f481OScI0jsv0CVxurfWlhUOg=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=pZ8vuSXHAwPRZQLGBKmDMt5OW4FPJEt1uQvikEIe7A6JADYVdRluNchFIh5SshSIg\n\te/d0lWC5Ur2A0spBF1e+wz7/0nEsEzVIB/zpbMqIKQrx2S5ht++cihOA5lHMTl9cP0\n\tGE+6a0sEqJdq3oqhu1r9yN/FBMcAeSkz9c6d4aro="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"pZ8vuSXH\"; dkim-atps=neutral","Message-ID":"<2b6ebd64-e194-bb9f-599a-a5ff4302fb40@ideasonboard.com>","Date":"Mon, 23 May 2022 14:19:45 +0300","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.9.1","Content-Language":"en-US","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<YnpnKWFt68HkwKqO@pendragon.ideasonboard.com>\n\t<02d30dc3-dc00-a66c-be78-c0c60c8aa3bf@ideasonboard.com>\n\t<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>","In-Reply-To":"<Ynp0xirXm4iaa3uC@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH] cam: convert to libfmt","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>","From":"Tomi Valkeinen via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23204,"web_url":"https://patchwork.libcamera.org/comment/23204/","msgid":"<0f26f4cb-a28f-fef7-9e9a-d552bcbdba65@gmx.de>","date":"2022-05-27T23:19:05","subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","submitter":{"id":111,"url":"https://patchwork.libcamera.org/api/people/111/","name":"Christian Rauch","email":"Rauch.Christian@gmx.de"},"content":"Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel:\n> On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote:\n>> Quoting Laurent Pinchart (2022-05-10 14:04:57)\n>>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n>>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n>>>>> Quoting Naushir Patuck (2022-05-10 10:49:14)\n>>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n>\n> [snip]\n>\n>>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and\n>>>>>> libfmt is a wonderful alternative.\n>>>>>> It also is close enough to the C++20 std::format implementation that\n>>>>>> eventual porting would be low effort.  So I am all for this change :)\n>>>\n>>> It's going to take a while before we can switch to C++20, but I'm\n>>> looking forward to it (and before that to some of the C++17 features\n>>> too, it would be nice to use std::optional in the public API).\n>>\n>>\n>> So, I've just seen as Christian said - we already do!\n>>\n>> It's used by color space ..\n>\n> Oops...\n>\n>> So we're faced with either reverting, or rolling back on the color-space\n>> change, or pushing forwards with C++17 on the public-api.\n>\n> Switching to C++17 worries me as it's fairly recent and may not be\n> easily available for all users. std::optional could easily rbe replaced\n> with a custom implementation in utils::optional if needed. I'm tempted\n> to leave std::optional in for now, and accept new users of std::optional\n> but no other C++17 APIs. Switching to utils::optional for ColorSpace\n> would cover new users of std::optional so we're not creating any new\n> liability.\n\nI think that C++17 is quite old by now (nearly 5 years) and all major\ncompilers support it for multiple versions and years now. Do you know of\nan example, where requiring C++17 would hinder the adaptation of\nlibcamera? Do any of the target system not support this standard?\n\nI think the use of std::optional to express invalid values is quite\nsignificant compared to the previous solution of using default\nconstructed values. Reimplementing this in libcamera would be a\nsolution, but it just adds development and maintenance costs for no\nobvious reason (to me).\n\n>\n>> Maybe this deserves it's own thread...\n>\n> Done :-)\n>\n>>>> I am all in for this change also, personally I would have changed to\n>>>> printf for now\n>>>> to have one less dependency (also an easy port to C++20 std::format). The\n>>>> dependency list is getting large, I noticed a build of mine failing\n>>>> recently because\n>>>> I didn't have libyaml.\n>>>\n>>> I've posted a patch that falls back to a subproject in that case. Would\n>>> you be able to give it a try ?\n>>>\n>>>> But std::format and libfmt are quite fast and anything is better than\n>>>> streams so +1.\n>>>>\n>>>>> I've never used (yet) {fmt}, but I've only heard good things about\n>>>>> performance, and of course it's headed into the standard, so I also\n>>>>> think there is some good merit to be found in this development.\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 0A2EFBD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 May 2022 23:19:09 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 27FF6633A5;\n\tSat, 28 May 2022 01:19:08 +0200 (CEST)","from mout.gmx.net (mout.gmx.net [212.227.15.19])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5FC3060DB0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 28 May 2022 01:19:06 +0200 (CEST)","from [192.168.1.209] ([92.18.80.244]) by mail.gmx.net (mrgmx004\n\t[212.227.17.190]) with ESMTPSA (Nemesis) id 1MAwbz-1o5TnM40YD-00BOk0\n\tfor\n\t<libcamera-devel@lists.libcamera.org>; Sat, 28 May 2022 01:19:06 +0200"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653693548;\n\tbh=3drtZ0vDwxf8cNS2xX6gIeGGoGFSqY1nLSn0Am5reEo=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=QMgDFO04czLVCdyf4Y/tKVuyKPgWbGGxiumTV3iopir1O3lQYrEN9oazSPWoW/hto\n\teS8U9VFq6KqHxZD/6M1kMo1TYeSA2law60l+QlfDL+6Wy2JFbrhNInrdNKE0Vegrh6\n\tfAiZWp8NmNZ6EC15pokL9+snO2IXfKjANUsYLFz7scCH8IUaLpak1Qpu5XwGU46m1S\n\tAqZiXL2oMAi+LTANigyUqUeI/rXviNlZ7/E41am3eTcxqJWd8nkvCbo7swT210iTZQ\n\tlZPEI68UVJcycAMa9OcTH1+Hn8I1Dolh65ThfKJ3/pMcte6UiGhClSEodLEboVqk5G\n\t6kyBnwg2dDNkA==","v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net;\n\ts=badeba3b8450; t=1653693546;\n\tbh=3drtZ0vDwxf8cNS2xX6gIeGGoGFSqY1nLSn0Am5reEo=;\n\th=X-UI-Sender-Class:Date:Subject:To:References:From:In-Reply-To;\n\tb=dY0Cc3HGQHyZGYNl3frq5dTcLJ3c6aoEZZ4VlhAamTdy8x7lnFjsKK56BModsSIJ7\n\tQboLq5ibDdQcDFDGUPD4lhF+UXFgNUNKPtQRh8wEWfvVgIfWmbkFgr7S1q+AUnos+p\n\tmaOehBUEO/i53vDXj6r8IBN9SQYsRwKebZUWlfkc="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=gmx.net header.i=@gmx.net\n\theader.b=\"dY0Cc3HG\"; dkim-atps=neutral","X-UI-Sender-Class":"01bb95c1-4bf8-414a-932a-4f6e2808ef9c","Message-ID":"<0f26f4cb-a28f-fef7-9e9a-d552bcbdba65@gmx.de>","Date":"Sat, 28 May 2022 00:19:05 +0100","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.9.1","Content-Language":"en-GB","To":"libcamera-devel@lists.libcamera.org","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>\n\t<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>\n\t<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>\n\t<165321410734.4128907.16683618909815271160@Monstersaurus>\n\t<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>","In-Reply-To":"<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"quoted-printable","X-Provags-ID":"V03:K1:pG8mx/AjRm1bpXNYbnjP9qK31jmQfrhdNy3weyTPC9h+W+55rvT\n\tGO0uNU2cTjJZ2nnilihVbyDyYjiMb8eBw+X+CldiOtZlu9xGH1PHEcbWUGmDwX7v6MguPky\n\t8fvZs9b8jF7bcYPPkHkNu1puQQk7vhRpzGcKpPcTzTKYRORIfDEWLLuRzBcUbzJfzFx5oTY\n\tKsBQLMcwl4sVbzMg5hxLw==","X-Spam-Flag":"NO","X-UI-Out-Filterresults":"notjunk:1; V03:K0:MpvDK1DZ6DI=:Fk8k3ER5IPO259ZQd5Uopu\n\tScCdG4/zucbUsKX8MxzswxHN1qI7pcggWZJU/ng6ogaXKumkZExvhzYJTKunS8VpTHcOtEbKP\n\tD2HV7Mk2ASt53rn+Z2f3bykQ2AXmaA1+bROw5ixADHkrdP6aMIyIUY+sO9327YnkrBfWdA+6R\n\tf0NTWNWnQVUOytq+7oOR/9YizU6P4gwlCYY0vMUcWG0LnfFUmwNAQl7WjyaHwuRgtJZh0ELHW\n\tYmW7WkfqWDIrUbvmQyy69xKZhhKzenCpZtYOXHmybFhPz3YKTiZWQi2s2JENu6W1yf8CLgPlh\n\tnPbDpAc5cGUo1Xdn4Cwp3WAhJBcIFFRwJCQ/XwqP+X7o7PHN/du40wsk2UlfH6Dfi2zR4gTCM\n\thiYACjwLKAD4HXSCH+9I3B0MAvrR+J+re/6J74G+7qi5bBmXWIAnmPetevzYzcTIEA+1mYljT\n\tS1hdO0Kz7UtuyG8hJ3hpPTcfu03NKJeb5++Qpx3ezTOhUNA0E8TxqxnorW4AlIqxXPrsTRHu9\n\tfj1Yx/r5IKce7GnUrRyGf67VBvvpgnEWPWutMFnNR9LvoVAOUGgIZqC8oB9IREoNrIBGukMqM\n\tMrbEIUENME+N4c1e0GOxUw8n6a9gMd3MwwOROEH5rOCx3Tiva99beEE7kTOLhCzfKYqxYsnGk\n\tTCWQV4INeUNucBfsbDgYlIi6Z3LZqqnABMOnA/cwK7ZuOKu0cM/H74BNO9hvLkCBeeXJOrtjz\n\tE0XcNhCHULrM79FLhjidRVVpWZW+SymFnXsneKaLfiwn/twpe65feuEgABM4Ku6tseE7wswtA\n\tOlqSwabkS68NsHX+VGiLf+zNVpdGVVcZJcsxIGIoyEZMNp1c1OUfp9A6GzVnIi8d+q1F7jQb0\n\ttAL5pGwSxQ3lsbjUwGUr/BnSKf7kNtKF+mMRStzynAZSOF6Ofr8yjckC0NP7dt0gCpO/KSIDu\n\t3hdOl3QToIURzb/VWWBVBnOuWv259eiReUboAZLLATI0jNMtJM1MHhnMTGCgXizmlrIZFNxqv\n\tQjlLm0AmMzgpRPHZJqokyFDDUMqDYsn8zmrjKlvvC8vN0nZYZUfDST3TbS3WZb31iZHuJUIgR\n\thTbxQlKLeovnoU3UJjwBYyHHI4k+6Fi/o+HArBRfNroEOPqZBxHnw30yg==","Subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","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>","From":"Christian Rauch via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Christian Rauch <Rauch.Christian@gmx.de>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23229,"web_url":"https://patchwork.libcamera.org/comment/23229/","msgid":"<CAOgh=Fw8Sug28+xhSsL70-6X8eWSvRmuLyYTSse=1iYehQPNPA@mail.gmail.com>","date":"2022-05-30T08:47:25","subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","submitter":{"id":101,"url":"https://patchwork.libcamera.org/api/people/101/","name":"Eric Curtin","email":"ecurtin@redhat.com"},"content":"On Sat, 28 May 2022 at 00:19, Christian Rauch via libcamera-devel\n<libcamera-devel@lists.libcamera.org> wrote:\n>\n>\n>\n> Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel:\n> > On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote:\n> >> Quoting Laurent Pinchart (2022-05-10 14:04:57)\n> >>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n> >>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n> >>>>> Quoting Naushir Patuck (2022-05-10 10:49:14)\n> >>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n> >\n> > [snip]\n> >\n> >>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and\n> >>>>>> libfmt is a wonderful alternative.\n> >>>>>> It also is close enough to the C++20 std::format implementation that\n> >>>>>> eventual porting would be low effort.  So I am all for this change :)\n> >>>\n> >>> It's going to take a while before we can switch to C++20, but I'm\n> >>> looking forward to it (and before that to some of the C++17 features\n> >>> too, it would be nice to use std::optional in the public API).\n> >>\n> >>\n> >> So, I've just seen as Christian said - we already do!\n> >>\n> >> It's used by color space ..\n> >\n> > Oops...\n> >\n> >> So we're faced with either reverting, or rolling back on the color-space\n> >> change, or pushing forwards with C++17 on the public-api.\n> >\n> > Switching to C++17 worries me as it's fairly recent and may not be\n> > easily available for all users. std::optional could easily rbe replaced\n> > with a custom implementation in utils::optional if needed. I'm tempted\n> > to leave std::optional in for now, and accept new users of std::optional\n> > but no other C++17 APIs. Switching to utils::optional for ColorSpace\n> > would cover new users of std::optional so we're not creating any new\n> > liability.\n>\n> I think that C++17 is quite old by now (nearly 5 years) and all major\n> compilers support it for multiple versions and years now. Do you know of\n> an example, where requiring C++17 would hinder the adaptation of\n> libcamera? Do any of the target system not support this standard?\n>\n> I think the use of std::optional to express invalid values is quite\n> significant compared to the previous solution of using default\n> constructed values. Reimplementing this in libcamera would be a\n> solution, but it just adds development and maintenance costs for no\n> obvious reason (to me).\n>\n> >\n> >> Maybe this deserves it's own thread...\n> >\n> > Done :-)\n> >\n> >>>> I am all in for this change also, personally I would have changed to\n> >>>> printf for now\n> >>>> to have one less dependency (also an easy port to C++20 std::format). The\n> >>>> dependency list is getting large, I noticed a build of mine failing\n> >>>> recently because\n> >>>> I didn't have libyaml.\n> >>>\n> >>> I've posted a patch that falls back to a subproject in that case. Would\n> >>> you be able to give it a try ?\n> >>>\n> >>>> But std::format and libfmt are quite fast and anything is better than\n> >>>> streams so +1.\n> >>>>\n> >>>>> I've never used (yet) {fmt}, but I've only heard good things about\n> >>>>> performance, and of course it's headed into the standard, so I also\n> >>>>> think there is some good merit to be found in this development.\n> >\n>\n\nOnly speaking for the red hat distros, every distro that has a package\nbuilt for libcamera or could have in future has at least C++17,\nincluding newly released LTS release RHEL9.\n\nGiven libcamera is still 0.0 it could be a nice opportunity to take.","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 EC5DCBD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 30 May 2022 08:47:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3CE4E65635;\n\tMon, 30 May 2022 10:47:47 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 56DE26040B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 30 May 2022 10:47:45 +0200 (CEST)","from mail-qt1-f200.google.com (mail-qt1-f200.google.com\n\t[209.85.160.200]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n\tus-mta-199-z3_gzwnEPZOtL_gv336URQ-1; Mon, 30 May 2022 04:47:42 -0400","by mail-qt1-f200.google.com with SMTP id\n\ta11-20020a05622a02cb00b002f3d23bdca2so9683708qtx.5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 30 May 2022 01:47:42 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653900467;\n\tbh=wLg+UoqGGi/clNXb43xgUPPmvhx274jZMchihCCDuwI=;\n\th=References:In-Reply-To:Date:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=bN+cGXgap0Ixb9HaQtgYX/bMtsJuPxGwGlWHnJS17apUQGRJjumMfAtcBSIWlrOLz\n\tAy0/XL5PFmSYMMdLLmTtFDqTzGOtfMg/HBOYYm+MHe/uM0UQYGSiWAVn70YKzG0C7Y\n\t0VOEwow2aV1SWH0ZP48i6w3P2RGCU9X9NKqCxC2WjbubslkIOvGQU9A5R0Md3UJcbE\n\tKn798sMeNodVNA1kmyt63xbMKEtOoH4Tt7ktGt5xI5P9ixuH37k/EEYhkmb57ROUeh\n\tumjAqNyPDT/CSw7W6RdlQuElqLELb30gCbGtdEvBdCt3zIf/JkNl4y67e13merDggU\n\twaXnFFGqlkbyQ==","v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1653900464;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=n6DuvQuVfsGI/mNlCjnWEt2a59uiHamTecRPD9t78lk=;\n\tb=f0d9cRKbXDV5wTYveYDzQsLnFsicYk7epZBGVlMOAulRvp4B+0nU5Wc725MfFirpA9q/Ms\n\t9RfKe/ZINVTM5GaO3Z63+lQEk53pUEF5zfgojC5LLa8b9BzP4mKnVsABFbkyC1idZDU6I+\n\tKw9MgmlNjiY2YE8iyp2Bpu4vcVt9NlQ="],"Authentication-Results":["lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=redhat.com\n\theader.i=@redhat.com header.b=\"f0d9cRKb\"; \n\tdkim-atps=neutral","relay.mimecast.com;\n\tauth=pass smtp.auth=CUSA124A263 smtp.mailfrom=ecurtin@redhat.com"],"X-MC-Unique":"z3_gzwnEPZOtL_gv336URQ-1","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=n6DuvQuVfsGI/mNlCjnWEt2a59uiHamTecRPD9t78lk=;\n\tb=hDmoumFKqfIv+20+LZgJspw5zi1ttJ/WX7uXkGDyLKPs1AjkWAknjw2o0jClhP947O\n\tVFMS4S+utovfNStFK1Lm9O36IJW7ozVvidmxyPpBYVXWJWwoYFS2Nf5dKrLf1XgSAE3N\n\tUi2TUrjBzu2BSfdI3S2ynxjcepnbsbwiyhLd3Wk1/xTn04nysjmgdpZIVguVWRNNIeGK\n\t7oAhFY7y8t/xv8vejRSmELAw6IEKAuJKvAkMQwlu/MehpCmH9FFOUHSLOYO3twHmq+pe\n\tBKfEt7PbQqFeZizf86Xhd+8XhLusqWLFJ2oDVUrwmkBPzwmcXZXSa6YGXSI4UbH4CIF5\n\tSDCg==","X-Gm-Message-State":"AOAM533NwKUDYI/Qd8zQtj6TAGaTGZ9YyVJCO7PfjxTsUAI8pyKvncS5\n\tsHQbUWb+B5Rhtan6slZoR1apdB1iilIEceIxhaIpHZubRi0sFrlGZ0KkMSmkGjMR11if8WNf9Ig\n\t/eZtrL0gmYUqSmoMZRarU7bqRs552tz316kJ4vOrw8N7/Koicqg==","X-Received":["by 2002:a05:6214:2267:b0:461:e790:e80f with SMTP id\n\tgs7-20020a056214226700b00461e790e80fmr45065926qvb.81.1653900461666; \n\tMon, 30 May 2022 01:47:41 -0700 (PDT)","by 2002:a05:6214:2267:b0:461:e790:e80f with SMTP id\n\tgs7-20020a056214226700b00461e790e80fmr45065917qvb.81.1653900461468;\n\tMon, 30 May 2022 01:47:41 -0700 (PDT)"],"X-Google-Smtp-Source":"ABdhPJzl5a6TsV2LdDM7c0OFQGV3eteerpZcja2zLoNyUQwDjSkzG3Hn/6VbSbNoYnVKWaXREujsT7rzLGaozATwg2U=","MIME-Version":"1.0","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>\n\t<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>\n\t<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>\n\t<165321410734.4128907.16683618909815271160@Monstersaurus>\n\t<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>\n\t<0f26f4cb-a28f-fef7-9e9a-d552bcbdba65@gmx.de>","In-Reply-To":"<0f26f4cb-a28f-fef7-9e9a-d552bcbdba65@gmx.de>","Date":"Mon, 30 May 2022 09:47:25 +0100","Message-ID":"<CAOgh=Fw8Sug28+xhSsL70-6X8eWSvRmuLyYTSse=1iYehQPNPA@mail.gmail.com>","To":"Christian Rauch <Rauch.Christian@gmx.de>","X-Mimecast-Spam-Score":"0","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","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>","From":"Eric Curtin via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Eric Curtin <ecurtin@redhat.com>","Cc":"Javier Martinez Canillas <fmartine@redhat.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23230,"web_url":"https://patchwork.libcamera.org/comment/23230/","msgid":"<YpSITTmGXU5OS2kn@pendragon.ideasonboard.com>","date":"2022-05-30T09:03:09","subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote:\n> On Sat, 28 May 2022 at 00:19, Christian Rauch wrote:\n> > Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel:\n> > > On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote:\n> > >> Quoting Laurent Pinchart (2022-05-10 14:04:57)\n> > >>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n> > >>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n> > >>>>> Quoting Naushir Patuck (2022-05-10 10:49:14)\n> > >>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n> > >\n> > > [snip]\n> > >\n> > >>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and\n> > >>>>>> libfmt is a wonderful alternative.\n> > >>>>>> It also is close enough to the C++20 std::format implementation that\n> > >>>>>> eventual porting would be low effort.  So I am all for this change :)\n> > >>>\n> > >>> It's going to take a while before we can switch to C++20, but I'm\n> > >>> looking forward to it (and before that to some of the C++17 features\n> > >>> too, it would be nice to use std::optional in the public API).\n> > >>\n> > >> So, I've just seen as Christian said - we already do!\n> > >>\n> > >> It's used by color space ..\n> > >\n> > > Oops...\n> > >\n> > >> So we're faced with either reverting, or rolling back on the color-space\n> > >> change, or pushing forwards with C++17 on the public-api.\n> > >\n> > > Switching to C++17 worries me as it's fairly recent and may not be\n> > > easily available for all users. std::optional could easily rbe replaced\n> > > with a custom implementation in utils::optional if needed. I'm tempted\n> > > to leave std::optional in for now, and accept new users of std::optional\n> > > but no other C++17 APIs. Switching to utils::optional for ColorSpace\n> > > would cover new users of std::optional so we're not creating any new\n> > > liability.\n> >\n> > I think that C++17 is quite old by now (nearly 5 years) and all major\n> > compilers support it for multiple versions and years now. Do you know of\n> > an example, where requiring C++17 would hinder the adaptation of\n> > libcamera? Do any of the target system not support this standard?\n\nThe main blocker used to tbe Chromium, which used C++14. It seems to\nhave moved to C++17 now, and we have also dropped the plan for direct\nintegration of libcamera support in Chromium (and Firefox) as they\nshould go through PipeWire and the XDG camera portal.\n\n> > I think the use of std::optional to express invalid values is quite\n> > significant compared to the previous solution of using default\n> > constructed values. Reimplementing this in libcamera would be a\n> > solution, but it just adds development and maintenance costs for no\n> > obvious reason (to me).\n\nWe can use std::optional (as it's there already). *If* C++17 support\nends up being an issue in the future, requiring us to move back to\nC++14, then it will be easy to make a custom implementation, but only if\nnecessary (we've done that with std::span already, which is only\navailable in C++20).\n\n> > >> Maybe this deserves it's own thread...\n> > >\n> > > Done :-)\n> > >\n> > >>>> I am all in for this change also, personally I would have changed to\n> > >>>> printf for now\n> > >>>> to have one less dependency (also an easy port to C++20 std::format). The\n> > >>>> dependency list is getting large, I noticed a build of mine failing\n> > >>>> recently because\n> > >>>> I didn't have libyaml.\n> > >>>\n> > >>> I've posted a patch that falls back to a subproject in that case. Would\n> > >>> you be able to give it a try ?\n> > >>>\n> > >>>> But std::format and libfmt are quite fast and anything is better than\n> > >>>> streams so +1.\n> > >>>>\n> > >>>>> I've never used (yet) {fmt}, but I've only heard good things about\n> > >>>>> performance, and of course it's headed into the standard, so I also\n> > >>>>> think there is some good merit to be found in this development.\n> \n> Only speaking for the red hat distros, every distro that has a package\n> built for libcamera or could have in future has at least C++17,\n> including newly released LTS release RHEL9.\n\nThat's good to know. The other question is whether all C++ applications\nthat may want to use libcamera can be compiled with C++17 or newer.\n\n> Given libcamera is still 0.0 it could be a nice opportunity to take.","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 62523BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 30 May 2022 09:03:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id AC57B65633;\n\tMon, 30 May 2022 11:03:13 +0200 (CEST)","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 2A82E6040B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 30 May 2022 11:03:12 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(lmontsouris-659-1-41-236.w92-154.abo.wanadoo.fr [92.154.76.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id C10316BD;\n\tMon, 30 May 2022 11:03:11 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653901393;\n\tbh=kYR/n6nPBhF40IE+giGucDEyaw9mY/Vz4dKRCvklPGk=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=iixIkHHJqa5tQpYoGttsw9EKAOkU7hc3CyXPCPYnbvFplDjr2vPjR+E72ER2MB+QP\n\tJp93qNj/kljvaBHu5KVCrNqU2HDus7BDYJeg5OXbWwrxm3JIPX4jU73wafvsMonhDf\n\tprCFEwNnwoDJpxBxpwYMVepHobo+A/PKYK68XUCoOmzn1BE8pWtzKcUAqXqinmeJ0u\n\tPOzsYVtL4Z+jogj0XZDf3xSX0a10Dq8NFLfuvIokm2PBcQV9tuAblr5dWDVhauy5yx\n\t1XYfK7a9CbattthXcwYFhIJhG51V3iscgBXO8/gLtpSDcHregB+bbrVkafqzA/KfBG\n\tBdTheykQY6GZA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653901391;\n\tbh=kYR/n6nPBhF40IE+giGucDEyaw9mY/Vz4dKRCvklPGk=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=i0VHnIJs1WC+Y3i+YqmmSyj4fxZPvIefeeMmnOvRiMREzp9AJ5Jf51DRXKqpkCp1m\n\tUWK952bbf3/cQQrV0sFWmEszu+th/32/wgQwKvPpjtQMOOj2g8tVg3Wkt1Mvk8A5j8\n\t4rg+51voU/RmcQIBf9TBwq8+4e83gdysNIvLonAg="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"i0VHnIJs\"; dkim-atps=neutral","Date":"Mon, 30 May 2022 12:03:09 +0300","To":"Eric Curtin <ecurtin@redhat.com>","Message-ID":"<YpSITTmGXU5OS2kn@pendragon.ideasonboard.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>\n\t<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>\n\t<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>\n\t<165321410734.4128907.16683618909815271160@Monstersaurus>\n\t<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>\n\t<0f26f4cb-a28f-fef7-9e9a-d552bcbdba65@gmx.de>\n\t<CAOgh=Fw8Sug28+xhSsL70-6X8eWSvRmuLyYTSse=1iYehQPNPA@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<CAOgh=Fw8Sug28+xhSsL70-6X8eWSvRmuLyYTSse=1iYehQPNPA@mail.gmail.com>","Subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"Javier Martinez Canillas <fmartine@redhat.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23245,"web_url":"https://patchwork.libcamera.org/comment/23245/","msgid":"<7a240f03-2879-27cb-75ae-6e35ca6b1d9a@gmx.de>","date":"2022-05-30T10:11:23","subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","submitter":{"id":111,"url":"https://patchwork.libcamera.org/api/people/111/","name":"Christian Rauch","email":"Rauch.Christian@gmx.de"},"content":"Am 30.05.22 um 10:03 schrieb Laurent Pinchart:\n> On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote:\n>> On Sat, 28 May 2022 at 00:19, Christian Rauch wrote:\n>>> Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel:\n>>>> On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote:\n>>>>> Quoting Laurent Pinchart (2022-05-10 14:04:57)\n>>>>>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n>>>>>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n>>>>>>>> Quoting Naushir Patuck (2022-05-10 10:49:14)\n>>>>>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n>>>>\n>>>> [snip]\n>>>>\n>>>>>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and\n>>>>>>>>> libfmt is a wonderful alternative.\n>>>>>>>>> It also is close enough to the C++20 std::format implementation that\n>>>>>>>>> eventual porting would be low effort.  So I am all for this change :)\n>>>>>>\n>>>>>> It's going to take a while before we can switch to C++20, but I'm\n>>>>>> looking forward to it (and before that to some of the C++17 features\n>>>>>> too, it would be nice to use std::optional in the public API).\n>>>>>\n>>>>> So, I've just seen as Christian said - we already do!\n>>>>>\n>>>>> It's used by color space ..\n>>>>\n>>>> Oops...\n>>>>\n>>>>> So we're faced with either reverting, or rolling back on the color-space\n>>>>> change, or pushing forwards with C++17 on the public-api.\n>>>>\n>>>> Switching to C++17 worries me as it's fairly recent and may not be\n>>>> easily available for all users. std::optional could easily rbe replaced\n>>>> with a custom implementation in utils::optional if needed. I'm tempted\n>>>> to leave std::optional in for now, and accept new users of std::optional\n>>>> but no other C++17 APIs. Switching to utils::optional for ColorSpace\n>>>> would cover new users of std::optional so we're not creating any new\n>>>> liability.\n>>>\n>>> I think that C++17 is quite old by now (nearly 5 years) and all major\n>>> compilers support it for multiple versions and years now. Do you know of\n>>> an example, where requiring C++17 would hinder the adaptation of\n>>> libcamera? Do any of the target system not support this standard?\n>\n> The main blocker used to tbe Chromium, which used C++14. It seems to\n> have moved to C++17 now, and we have also dropped the plan for direct\n> integration of libcamera support in Chromium (and Firefox) as they\n> should go through PipeWire and the XDG camera portal.\n>\n>>> I think the use of std::optional to express invalid values is quite\n>>> significant compared to the previous solution of using default\n>>> constructed values. Reimplementing this in libcamera would be a\n>>> solution, but it just adds development and maintenance costs for no\n>>> obvious reason (to me).\n>\n> We can use std::optional (as it's there already). *If* C++17 support\n> ends up being an issue in the future, requiring us to move back to\n> C++14, then it will be easy to make a custom implementation, but only if\n> necessary (we've done that with std::span already, which is only\n> available in C++20).\n>\n>>>>> Maybe this deserves it's own thread...\n>>>>\n>>>> Done :-)\n>>>>\n>>>>>>> I am all in for this change also, personally I would have changed to\n>>>>>>> printf for now\n>>>>>>> to have one less dependency (also an easy port to C++20 std::format). The\n>>>>>>> dependency list is getting large, I noticed a build of mine failing\n>>>>>>> recently because\n>>>>>>> I didn't have libyaml.\n>>>>>>\n>>>>>> I've posted a patch that falls back to a subproject in that case. Would\n>>>>>> you be able to give it a try ?\n>>>>>>\n>>>>>>> But std::format and libfmt are quite fast and anything is better than\n>>>>>>> streams so +1.\n>>>>>>>\n>>>>>>>> I've never used (yet) {fmt}, but I've only heard good things about\n>>>>>>>> performance, and of course it's headed into the standard, so I also\n>>>>>>>> think there is some good merit to be found in this development.\n>>\n>> Only speaking for the red hat distros, every distro that has a package\n>> built for libcamera or could have in future has at least C++17,\n>> including newly released LTS release RHEL9.\n>\n> That's good to know. The other question is whether all C++ applications\n> that may want to use libcamera can be compiled with C++17 or newer.\n\nIsn't C++17 backwards compatible? My understanding is that every earlier\nC++ standard (e.g. C++11) should be able to compile with a C++17 (or\nlater) compiler.\n\n>\n>> Given libcamera is still 0.0 it could be a nice opportunity to take.\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 16D56BD161\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 30 May 2022 10:11:27 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 560F965633;\n\tMon, 30 May 2022 12:11:26 +0200 (CEST)","from mout.gmx.net (mout.gmx.net [212.227.15.15])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3B0E260411\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 30 May 2022 12:11:25 +0200 (CEST)","from [192.168.1.209] ([92.18.80.244]) by mail.gmx.net (mrgmx005\n\t[212.227.17.190]) with ESMTPSA (Nemesis) id 1Mv31W-1ndwzc2IdM-00qyVZ;\n\tMon, 30 May 2022 12:11:24 +0200"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653905486;\n\tbh=lbODdDiaJQ0ISX5fbDEa5GWTBCodwrw7RPqGk9/XNsc=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=Zlj+m/HMWqtZqU01xKMPyN/Vft/huKAzt6dnjXXeCLsVHZ03QFpHxr3ALUN/b19t7\n\tyyKYtZPpvQghcQ5G8Y3s4neSbtLU2n+pddUXrAyCGDkYVmEnJRBlcECCTUdNVxFVby\n\tCL5YCx5aOagwmpqmsl3zYnIncsbRaocPRWzrYizn/18+ncMIqLd102SS02kD8uyqAl\n\t2KH3cLNEPxxN1C1hhAsLGs59bDHPeLXNNPXx/wL8hsf7UYC8sT1l2AK1yWomOA9qta\n\tjgdf2zVrjjZgdEZTzM05CX8reI6JLlb6FItR+qXI/d8+1iSwo6coqhcXUldJda57aT\n\tr1pCZDL9g/ajg==","v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net;\n\ts=badeba3b8450; t=1653905484;\n\tbh=lbODdDiaJQ0ISX5fbDEa5GWTBCodwrw7RPqGk9/XNsc=;\n\th=X-UI-Sender-Class:Date:Subject:To:Cc:References:From:In-Reply-To;\n\tb=iHa21gfa0bfFygmfsnSv3F68sJ7uG5c2HVzZo467eWGeQrbRmDh9do++394rIATxq\n\t5fLOD/7iWB/h4LuL7LnVMEkqvFol9dPiJm6xeddIimpzeS2FOCP31LbnZywKFc3R23\n\tmiUnD8pE8hXCXNtsonUL1m4pVWogqxGJ5aiIdYrA="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=gmx.net header.i=@gmx.net\n\theader.b=\"iHa21gfa\"; dkim-atps=neutral","X-UI-Sender-Class":"01bb95c1-4bf8-414a-932a-4f6e2808ef9c","Message-ID":"<7a240f03-2879-27cb-75ae-6e35ca6b1d9a@gmx.de>","Date":"Mon, 30 May 2022 11:11:23 +0100","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.9.1","Content-Language":"en-GB","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tEric Curtin <ecurtin@redhat.com>","References":"<20220510071617.42227-1-tomi.valkeinen@ideasonboard.com>\n\t<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>\n\t<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>\n\t<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>\n\t<165321410734.4128907.16683618909815271160@Monstersaurus>\n\t<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>\n\t<0f26f4cb-a28f-fef7-9e9a-d552bcbdba65@gmx.de>\n\t<CAOgh=Fw8Sug28+xhSsL70-6X8eWSvRmuLyYTSse=1iYehQPNPA@mail.gmail.com>\n\t<YpSITTmGXU5OS2kn@pendragon.ideasonboard.com>","In-Reply-To":"<YpSITTmGXU5OS2kn@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"quoted-printable","X-Provags-ID":"V03:K1:CDwtWhordDTFtpLxvwt6WKhHd1XCGwyObTR6xIAFSTnE+YnjyNm\n\tez1HaNRJgz06zM5KOTQwzP+gZzfp5HKNf1RGMrTJsNHR+jzod1jIIGUAq9ppHFj0ihIGV0A\n\trDT5yMlfDttSwhLxKAXWUI0cpaqaWjWvkcRPNOL6bw1KbudrHITPPHhj1ahGfZpcXN1Hy75\n\tZO+nTyDG8jec7htj4dSNg==","X-Spam-Flag":"NO","X-UI-Out-Filterresults":"notjunk:1; V03:K0:mG1TmG1UOhg=:96DUcauTeWjvutafUMJ7Qo\n\twoYygJeER7t/i7wtL9VtynTbeZVEUThJwRhylmuq6sS2WSUDuybRgvK7qZvoaGPhrr20Tt77e\n\tUMSTAIKcahDZt+Kq2f/BvJB8pRxbMh77K8+T8Sg2+4b3nnYA1ywdPGfSATDDuv7uHNM5682SN\n\tfpuZQwoc16bJvli2dy4I9TwyEBp2/R+ghK2JdAqol9g5Uwd4btdFK3/euAS5FUJ+/3PdQxgHn\n\tRYN+enHB8dSc5JSyErNEKIN+c4qMuuroP2bPXo+v3vFQdsJHUsDHI/UBYmckdR6k5Xe65m5Op\n\tu9UnQ/sG/V5CPKKPi8XLTkzIZ2LqIFWqPCOYmMjw9+rXq40gM1r5j7O6xCOIQojv1rKSqPDkF\n\tBUsn6vRUu38+vFc5CPVZawHPXGKQiJF1yfV0nQX4WUkHnR5P1EGAUDUDLaVl1UpLYIS1g5smp\n\tXpEdfbkpFonFAEWC5aQvJ2YqojHixAgkA+Eyw7hoTrQRl9ai6+ABWgiIiPiPBbaUrq/MGlQ/L\n\tGSyPMiU5XU8ikOcvFthWFvO60+JupFNDlwVnXN4arhbKeWyoVu1frg9VWKjKaLJyzwnAwESUe\n\tVHtt0UqSLAAA2zOC+68EpkIUOlexeevfLmakMLsfZrLnergJcWff4T1dHevZZw6SoGgPhaqhH\n\tPpbKAJy51LgUMuCuywl846paDzRthNh+S7v5WknEP20K/BFE/i5mrWhC4ORgND6rTtZUZTYVa\n\t2qxb601Qx1TgakXgn1Q7aptI5J2+odmvaS/TeZiIrHxluYMNDEVy0quz3hOxQDFBla2+z5RoS\n\tWX1sHpe4Q15TIuXUnv8e8GTRin4SHmVbjxt7qMjTMzFXdo/xYUqZrCjZou69C/X+0pg0CpqG2\n\tlH4R25PsiCaeBrFUiO4Vq3ZVXxBShrunOCTuSnHj+taNloJ92FicwFaGZXs+1gtyO/Z3RDfJu\n\tAH8z9I6U57vBANasphzI1oYwms1Yg2bmbr0z3JzmmfAChVTjgwnJ4MCdOZ1hdt9/5kPrcoVJ5\n\tkSFoPF6+PitwF/xFQUM/4yjZpp4vVqFNnQKLHy2tpjAB6iKLQW4NtbmBqlkbtKh7v4B5fpvvO\n\tRYndi0e7mbq9Ps5XNAuGUY3s0roipAoRgorEf8iVljybUqUVehjG605TA==","Subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","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>","From":"Christian Rauch via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Christian Rauch <Rauch.Christian@gmx.de>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23247,"web_url":"https://patchwork.libcamera.org/comment/23247/","msgid":"<YpSeWne89ystM6/w@pendragon.ideasonboard.com>","date":"2022-05-30T10:37:14","subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Christian,\n\nOn Mon, May 30, 2022 at 11:11:23AM +0100, Christian Rauch wrote:\n> Am 30.05.22 um 10:03 schrieb Laurent Pinchart:\n> > On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote:\n> >> On Sat, 28 May 2022 at 00:19, Christian Rauch wrote:\n> >>> Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel:\n> >>>> On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote:\n> >>>>> Quoting Laurent Pinchart (2022-05-10 14:04:57)\n> >>>>>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n> >>>>>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n> >>>>>>>> Quoting Naushir Patuck (2022-05-10 10:49:14)\n> >>>>>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n> >>>>\n> >>>> [snip]\n> >>>>\n> >>>>>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and\n> >>>>>>>>> libfmt is a wonderful alternative.\n> >>>>>>>>> It also is close enough to the C++20 std::format implementation that\n> >>>>>>>>> eventual porting would be low effort.  So I am all for this change :)\n> >>>>>>\n> >>>>>> It's going to take a while before we can switch to C++20, but I'm\n> >>>>>> looking forward to it (and before that to some of the C++17 features\n> >>>>>> too, it would be nice to use std::optional in the public API).\n> >>>>>\n> >>>>> So, I've just seen as Christian said - we already do!\n> >>>>>\n> >>>>> It's used by color space ..\n> >>>>\n> >>>> Oops...\n> >>>>\n> >>>>> So we're faced with either reverting, or rolling back on the color-space\n> >>>>> change, or pushing forwards with C++17 on the public-api.\n> >>>>\n> >>>> Switching to C++17 worries me as it's fairly recent and may not be\n> >>>> easily available for all users. std::optional could easily rbe replaced\n> >>>> with a custom implementation in utils::optional if needed. I'm tempted\n> >>>> to leave std::optional in for now, and accept new users of std::optional\n> >>>> but no other C++17 APIs. Switching to utils::optional for ColorSpace\n> >>>> would cover new users of std::optional so we're not creating any new\n> >>>> liability.\n> >>>\n> >>> I think that C++17 is quite old by now (nearly 5 years) and all major\n> >>> compilers support it for multiple versions and years now. Do you know of\n> >>> an example, where requiring C++17 would hinder the adaptation of\n> >>> libcamera? Do any of the target system not support this standard?\n> >\n> > The main blocker used to tbe Chromium, which used C++14. It seems to\n> > have moved to C++17 now, and we have also dropped the plan for direct\n> > integration of libcamera support in Chromium (and Firefox) as they\n> > should go through PipeWire and the XDG camera portal.\n> >\n> >>> I think the use of std::optional to express invalid values is quite\n> >>> significant compared to the previous solution of using default\n> >>> constructed values. Reimplementing this in libcamera would be a\n> >>> solution, but it just adds development and maintenance costs for no\n> >>> obvious reason (to me).\n> >\n> > We can use std::optional (as it's there already). *If* C++17 support\n> > ends up being an issue in the future, requiring us to move back to\n> > C++14, then it will be easy to make a custom implementation, but only if\n> > necessary (we've done that with std::span already, which is only\n> > available in C++20).\n> >\n> >>>>> Maybe this deserves it's own thread...\n> >>>>\n> >>>> Done :-)\n> >>>>\n> >>>>>>> I am all in for this change also, personally I would have changed to\n> >>>>>>> printf for now\n> >>>>>>> to have one less dependency (also an easy port to C++20 std::format). The\n> >>>>>>> dependency list is getting large, I noticed a build of mine failing\n> >>>>>>> recently because\n> >>>>>>> I didn't have libyaml.\n> >>>>>>\n> >>>>>> I've posted a patch that falls back to a subproject in that case. Would\n> >>>>>> you be able to give it a try ?\n> >>>>>>\n> >>>>>>> But std::format and libfmt are quite fast and anything is better than\n> >>>>>>> streams so +1.\n> >>>>>>>\n> >>>>>>>> I've never used (yet) {fmt}, but I've only heard good things about\n> >>>>>>>> performance, and of course it's headed into the standard, so I also\n> >>>>>>>> think there is some good merit to be found in this development.\n> >>\n> >> Only speaking for the red hat distros, every distro that has a package\n> >> built for libcamera or could have in future has at least C++17,\n> >> including newly released LTS release RHEL9.\n> >\n> > That's good to know. The other question is whether all C++ applications\n> > that may want to use libcamera can be compiled with C++17 or newer.\n> \n> Isn't C++17 backwards compatible? My understanding is that every earlier\n> C++ standard (e.g. C++11) should be able to compile with a C++17 (or\n> later) compiler.\n\nNot quite I'm afraid.\n\n--------\n#include <map>\t/* This includes <optional> indirectly in libc++ */\n\nusing namespace std;\n\ntemplate<class T>\nclass optional\n{\npublic:\n        optional()\n        {\n        }\n};\n\nint main()\n{\n        optional<int> opt;\n\n        return 0;\n}\n--------\n\n$ clang++ -W -Wall -stdlib=libc++ -std=c++14 -o version version.cpp\n$ clang++ -W -Wall -stdlib=libc++ -std=c++17 -o version version.cpp\nversion.cpp:16:2: error: reference to 'optional' is ambiguous\n        optional<int> opt;\n        ^\nversion.cpp:6:7: note: candidate found by name lookup is 'optional'\nclass optional\n      ^\n/usr/include/c++/v1/optional:590:7: note: candidate found by name lookup is 'std::optional'\nclass optional\n      ^\n1 error generated.\n\n> >> Given libcamera is still 0.0 it could be a nice opportunity to take.","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 333E9C3256\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 30 May 2022 10:37:19 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 94FF065635;\n\tMon, 30 May 2022 12:37:18 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 81BBC60411\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 30 May 2022 12:37:17 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(lmontsouris-659-1-41-236.w92-154.abo.wanadoo.fr [92.154.76.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3980C6DF;\n\tMon, 30 May 2022 12:37:17 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653907038;\n\tbh=lVyqWZtdTCFcMVbLmsCokI5xWBvJWkWmbetRBY9APYs=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=yaGYzjzLTztUdxltfkDO4pkiGL+Ow7PnsGkPIB4E15kzu6KUA8kFy47ptZi1Fqfnt\n\t4dkvyhhcvPUIgvNatyQuyPtvxzBeAD42kfsSKT6moDQSVj14iukO9BqPwzvUQ6FvR5\n\tZFr5APTEftpln/ddrs5Af4X6JbP6uvWhsF/h1em6huYQ8LoFMTcCVBqoMij2tOkph2\n\t8uTsrNNosyTXFU8iCUN+d3JwGFYrkvy4bzyVnH/9xgeIkMx0MFt+3idsZ+vWkrKaA3\n\tOcxWsRnBaAAOQ0OxIKglAajl5pK8TaSEcgdMGbol9DPXSmVRmhG5FZwYr7inyYW1+x\n\trzGWprK+NYjZA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1653907037;\n\tbh=lVyqWZtdTCFcMVbLmsCokI5xWBvJWkWmbetRBY9APYs=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=JabJTjTwStzIUFSp2uiqRQrqDYv0ovcdZyeOv0gqgyijmT1JPPJUtZQhqjavw+6eK\n\tK1wbhpI8e22ZV8mItd84l2EV8h1rm5CkHYvjRPQoBTLW+mAX6NV4D37fvkaV8noiOS\n\t6PulNMHoWNs6yvLjQj2MmRtT/1HslTJn6DdwEy4I="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"JabJTjTw\"; dkim-atps=neutral","Date":"Mon, 30 May 2022 13:37:14 +0300","To":"Christian Rauch <Rauch.Christian@gmx.de>","Message-ID":"<YpSeWne89ystM6/w@pendragon.ideasonboard.com>","References":"<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>\n\t<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>\n\t<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>\n\t<165321410734.4128907.16683618909815271160@Monstersaurus>\n\t<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>\n\t<0f26f4cb-a28f-fef7-9e9a-d552bcbdba65@gmx.de>\n\t<CAOgh=Fw8Sug28+xhSsL70-6X8eWSvRmuLyYTSse=1iYehQPNPA@mail.gmail.com>\n\t<YpSITTmGXU5OS2kn@pendragon.ideasonboard.com>\n\t<7a240f03-2879-27cb-75ae-6e35ca6b1d9a@gmx.de>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<7a240f03-2879-27cb-75ae-6e35ca6b1d9a@gmx.de>","Subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23255,"web_url":"https://patchwork.libcamera.org/comment/23255/","msgid":"<a751ea72-3663-be8b-2246-8b4164802b03@gmx.de>","date":"2022-05-30T22:20:06","subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","submitter":{"id":111,"url":"https://patchwork.libcamera.org/api/people/111/","name":"Christian Rauch","email":"Rauch.Christian@gmx.de"},"content":"Hi Laurent,\n\nAm 30.05.22 um 11:37 schrieb Laurent Pinchart:\n> Hi Christian,\n>\n> On Mon, May 30, 2022 at 11:11:23AM +0100, Christian Rauch wrote:\n>> Am 30.05.22 um 10:03 schrieb Laurent Pinchart:\n>>> On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote:\n>>>> On Sat, 28 May 2022 at 00:19, Christian Rauch wrote:\n>>>>> Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel:\n>>>>>> On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote:\n>>>>>>> Quoting Laurent Pinchart (2022-05-10 14:04:57)\n>>>>>>>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n>>>>>>>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n>>>>>>>>>> Quoting Naushir Patuck (2022-05-10 10:49:14)\n>>>>>>>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n>>>>>>\n>>>>>> [snip]\n>>>>>>\n>>>>>>>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and\n>>>>>>>>>>> libfmt is a wonderful alternative.\n>>>>>>>>>>> It also is close enough to the C++20 std::format implementation that\n>>>>>>>>>>> eventual porting would be low effort.  So I am all for this change :)\n>>>>>>>>\n>>>>>>>> It's going to take a while before we can switch to C++20, but I'm\n>>>>>>>> looking forward to it (and before that to some of the C++17 features\n>>>>>>>> too, it would be nice to use std::optional in the public API).\n>>>>>>>\n>>>>>>> So, I've just seen as Christian said - we already do!\n>>>>>>>\n>>>>>>> It's used by color space ..\n>>>>>>\n>>>>>> Oops...\n>>>>>>\n>>>>>>> So we're faced with either reverting, or rolling back on the color-space\n>>>>>>> change, or pushing forwards with C++17 on the public-api.\n>>>>>>\n>>>>>> Switching to C++17 worries me as it's fairly recent and may not be\n>>>>>> easily available for all users. std::optional could easily rbe replaced\n>>>>>> with a custom implementation in utils::optional if needed. I'm tempted\n>>>>>> to leave std::optional in for now, and accept new users of std::optional\n>>>>>> but no other C++17 APIs. Switching to utils::optional for ColorSpace\n>>>>>> would cover new users of std::optional so we're not creating any new\n>>>>>> liability.\n>>>>>\n>>>>> I think that C++17 is quite old by now (nearly 5 years) and all major\n>>>>> compilers support it for multiple versions and years now. Do you know of\n>>>>> an example, where requiring C++17 would hinder the adaptation of\n>>>>> libcamera? Do any of the target system not support this standard?\n>>>\n>>> The main blocker used to tbe Chromium, which used C++14. It seems to\n>>> have moved to C++17 now, and we have also dropped the plan for direct\n>>> integration of libcamera support in Chromium (and Firefox) as they\n>>> should go through PipeWire and the XDG camera portal.\n>>>\n>>>>> I think the use of std::optional to express invalid values is quite\n>>>>> significant compared to the previous solution of using default\n>>>>> constructed values. Reimplementing this in libcamera would be a\n>>>>> solution, but it just adds development and maintenance costs for no\n>>>>> obvious reason (to me).\n>>>\n>>> We can use std::optional (as it's there already). *If* C++17 support\n>>> ends up being an issue in the future, requiring us to move back to\n>>> C++14, then it will be easy to make a custom implementation, but only if\n>>> necessary (we've done that with std::span already, which is only\n>>> available in C++20).\n>>>\n>>>>>>> Maybe this deserves it's own thread...\n>>>>>>\n>>>>>> Done :-)\n>>>>>>\n>>>>>>>>> I am all in for this change also, personally I would have changed to\n>>>>>>>>> printf for now\n>>>>>>>>> to have one less dependency (also an easy port to C++20 std::format). The\n>>>>>>>>> dependency list is getting large, I noticed a build of mine failing\n>>>>>>>>> recently because\n>>>>>>>>> I didn't have libyaml.\n>>>>>>>>\n>>>>>>>> I've posted a patch that falls back to a subproject in that case. Would\n>>>>>>>> you be able to give it a try ?\n>>>>>>>>\n>>>>>>>>> But std::format and libfmt are quite fast and anything is better than\n>>>>>>>>> streams so +1.\n>>>>>>>>>\n>>>>>>>>>> I've never used (yet) {fmt}, but I've only heard good things about\n>>>>>>>>>> performance, and of course it's headed into the standard, so I also\n>>>>>>>>>> think there is some good merit to be found in this development.\n>>>>\n>>>> Only speaking for the red hat distros, every distro that has a package\n>>>> built for libcamera or could have in future has at least C++17,\n>>>> including newly released LTS release RHEL9.\n>>>\n>>> That's good to know. The other question is whether all C++ applications\n>>> that may want to use libcamera can be compiled with C++17 or newer.\n>>\n>> Isn't C++17 backwards compatible? My understanding is that every earlier\n>> C++ standard (e.g. C++11) should be able to compile with a C++17 (or\n>> later) compiler.\n>\n> Not quite I'm afraid.\n>\n> --------\n> #include <map>\t/* This includes <optional> indirectly in libc++ */\n>\n> using namespace std;\n>\n> template<class T>\n> class optional\n> {\n> public:\n>         optional()\n>         {\n>         }\n> };\n>\n> int main()\n> {\n>         optional<int> opt;\n>\n>         return 0;\n> }\n\nRight, this is clashing because of ambiguous symbols from different\nnamespaces. You have a point there. I only thought in terms of API\ncompatibility there.\n\nAnyway, libcamera already requires C++17 and uses std::optional. I would\nlike to push for a decision if libcamera:\nA) continues to use C++17 and std::optional\nB) reverts those changes and implements a custom \"optional\"\n\nI am in favour of keeping the status quo of using std::optional. The\nreason is that I think that not many projects (if any) will be affected\nby these kinds of ambiguity issues above. Resorting to a custom\nsolution, just to switch to std::optional in the public API later\n(when?), will cause more breakage issues. A custom temporary solution\nwill also require someone to implement and maintain this, knowing that\nit will be replaced eventually anyway.\n\nI simply think that the benefits outweigh the potential issues. Using\nstd::optional, for example as control value return type, improves the\ndescriptiveness of the API. While on the other side, there seem to be no\nprojects struggling with the current C++17/std::optional situation.\n\nSorry for pushing for a solution, but I really would like to continue\nwith my original patch set :-)\n\nBest,\nChristian\n\n> --------\n>\n> $ clang++ -W -Wall -stdlib=libc++ -std=c++14 -o version version.cpp\n> $ clang++ -W -Wall -stdlib=libc++ -std=c++17 -o version version.cpp\n> version.cpp:16:2: error: reference to 'optional' is ambiguous\n>         optional<int> opt;\n>         ^\n> version.cpp:6:7: note: candidate found by name lookup is 'optional'\n> class optional\n>       ^\n> /usr/include/c++/v1/optional:590:7: note: candidate found by name lookup is 'std::optional'\n> class optional\n>       ^\n> 1 error generated.\n>\n>>>> Given libcamera is still 0.0 it could be a nice opportunity to take.\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 0DC46BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 30 May 2022 22:20:10 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 343AE65635;\n\tTue, 31 May 2022 00:20:09 +0200 (CEST)","from mout.gmx.net (mout.gmx.net [212.227.15.15])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6617260415\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 31 May 2022 00:20:07 +0200 (CEST)","from [192.168.1.209] ([92.18.80.244]) by mail.gmx.net (mrgmx005\n\t[212.227.17.190]) with ESMTPSA (Nemesis) id 1MTzf6-1oMiIO3IPa-00QxEA\n\tfor\n\t<libcamera-devel@lists.libcamera.org>; Tue, 31 May 2022 00:20:06 +0200"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1653949209;\n\tbh=V/2S/XIA2kQsaSpOcYPRSYXPs9Zh9udqx77z1iUyFLw=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=YfFSxWKZdZCM9rlX8cl4yZ0UPhnPblrRrxbDIFPQF+LGfjoDSV6fbv3YjvTj0aV86\n\tfKf+X2/QgwJnQfbLWgq+QmaZf3+qBBh0t/XmnPyPO+7cUuBrreQVr19LvKivsdJD0f\n\tGUqe8o6ALcNZtGM5aGGPTz+bDmdzd6+fdfZMiqaxSoMKTpacqTNIRKdFwFPTfY/KSO\n\tCbADe9h2udw5jqTlaG7OBBGngAQYCqE4jP9wbxrZVp9jmU+ZIGBdYqiicj+vvF4PhG\n\tduNrbV11uLEotsbStr7T7+7nD5bSgX8+EsSRRHulwA1RcRIJuf9G67B8B39AuIsKrC\n\tZiDW7GeU2efww==","v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net;\n\ts=badeba3b8450; t=1653949207;\n\tbh=V/2S/XIA2kQsaSpOcYPRSYXPs9Zh9udqx77z1iUyFLw=;\n\th=X-UI-Sender-Class:Date:Subject:To:References:From:In-Reply-To;\n\tb=CgWMA4PSTbeitUMV4uN/xghEAfjcZIOubm7xoUZHnfI2bNd8WTdLhT0iLbLK668On\n\txhaiLXtOl2gs05OIBvqbEMfx0nIyz4WODXWNmaSV3h1xlTGhAAAl49bRWxMUnO8r8R\n\tNQEB9dPrc7rdq8WUU+MsYDDuA7aeZyQ4c3glK4t4="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=gmx.net header.i=@gmx.net\n\theader.b=\"CgWMA4PS\"; dkim-atps=neutral","X-UI-Sender-Class":"01bb95c1-4bf8-414a-932a-4f6e2808ef9c","Message-ID":"<a751ea72-3663-be8b-2246-8b4164802b03@gmx.de>","Date":"Mon, 30 May 2022 23:20:06 +0100","MIME-Version":"1.0","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101\n\tThunderbird/91.9.1","Content-Language":"en-GB","To":"libcamera devel <libcamera-devel@lists.libcamera.org>","References":"<CAEmqJPrJpHtg4M9R-wWt0PO1vVEpc4baVNa_TbGawDqgzSA3NA@mail.gmail.com>\n\t<165217739174.2416244.12652784382118148891@Monstersaurus>\n\t<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>\n\t<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>\n\t<165321410734.4128907.16683618909815271160@Monstersaurus>\n\t<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>\n\t<0f26f4cb-a28f-fef7-9e9a-d552bcbdba65@gmx.de>\n\t<CAOgh=Fw8Sug28+xhSsL70-6X8eWSvRmuLyYTSse=1iYehQPNPA@mail.gmail.com>\n\t<YpSITTmGXU5OS2kn@pendragon.ideasonboard.com>\n\t<7a240f03-2879-27cb-75ae-6e35ca6b1d9a@gmx.de>\n\t<YpSeWne89ystM6/w@pendragon.ideasonboard.com>","In-Reply-To":"<YpSeWne89ystM6/w@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"quoted-printable","X-Provags-ID":"V03:K1:Q7T06T/wwRExSAMzM+FB4VJO0PxLmcNVzgxk6a/5iawzCTq8Hy2\n\ttngQecWs48VHCy01AK91jjME7JTsqtPZ21UJZ7Wu2L7BhxcP142hGRtreI11MFb1XrLonXF\n\taG2J7jDGqGgr2i48azl3RXP/Lhyab91XnVA3YLXqBE7SboUj3kPn/+OgctDrCrbz3pzfFJS\n\tEX9dGx5N/zFiI5kUE2wdw==","X-Spam-Flag":"NO","X-UI-Out-Filterresults":"notjunk:1; V03:K0:FzqOR+FUpyE=:EfHeGiajdJDAIBmqMQfJrA\n\tkrwkvSR2R6VdsRGmJaCfhC/eFMJRGdaEPGlOuw7SRkI0PSzH9lNoV0B5z5e9frigO5WjpSj1/\n\tcq+gz1IC/2pIWginnZhlD2ccqpk3RBq71z5fQKSfNd1EdPkLp9JyPMJFD8GFSeiG7KjHL4vA9\n\taFxe56JZOocPpvRD7zLZs9fFWvkIzM3MC0pHQT33fSfu+MELScEAx3iTIY6soIUX5JFK6GM0X\n\tNqFLmIt12Hb6XBO7fq5id0adIKf60R11X/jA36u3Tybs2M3qzQpBI9SMlnspmZQx3TJpS0ITe\n\tu3Mz4EETl2fqqHix5df3IFNntRe/b+WqisjLYL5brQ3D/ev/NGW/rEd6u/+VCmeDcGQsPlK3U\n\tCVz1a90MrqEIgr42/cOLk6YZTOEKqnlQAJtB7IobCQlCaS0OgKO7Km7eVDa2GXcKtD3ypp9VU\n\tvVHzIAN0GGTlq0b4i0tKMJly7zo4KwNITsgnfJ6b3cOSe1bc8HF1nXUuMccPdneEoJ8xl9bje\n\tUXPjxTYnZMyyo1/vZPqA8i+BdySjgOMajQzYihzFC2uEEVxGPovMTTflrvoLD8kXk5KelPs3/\n\tpGchSXHOJYUKGou3zZ3AKyqhm50hQofX9WEA8SN27m1bWkLYkyoJ8A9zYYPBd8a6OEeYT6aoK\n\tYY++LTPQE5o0CTzxif2zEm3YbbFQf8u6hS74AlNu1rEnvgaBuXGLVbhDaNoYwaI1DcYAycvwx\n\tw7dMbvzPr1DelZHi9JGAdbuXexMPaLaWIyLWYXbhYKD4BOaLpoLWw1quVVgVSiRLsisaIhE1h\n\tr0PpRjVvuYWoxKH+QKcKwRsSfLwzi5J/A1ZILvz85w4YMneDy7Ov57KdooOKPtXp0E4VqT6oI\n\tKaBZk9yghdZFeG/tVHrRBBRgxtAaPqNCL5tD63uElpErJWQnIdFfxfqIG/myp8FL/zK8riqdn\n\tBNCIbiHOCs4yii+02Q7f/z6rnQE4PMCj6uVevooIVHPcNUqCaHkJHiohI1YVlcJxZk34DCYjp\n\tte7mWnJQ3nh79HTpR4uqQvpYYQjaW+l/6WOLp2Iv8JiIyFaDzBBl/aUjNg7ueQP6nGZpZ9IB2\n\tiLF9mVNH2IwMOh8mRvmr1NbVehppKS62ROdo6GzDJrZfSKKNBSX6YpqUA==","Subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","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>","From":"Christian Rauch via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Christian Rauch <Rauch.Christian@gmx.de>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":23280,"web_url":"https://patchwork.libcamera.org/comment/23280/","msgid":"<YpcqgOzcjP0nIgco@pendragon.ideasonboard.com>","date":"2022-06-01T08:59:44","subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Christian,\n\nOn Mon, May 30, 2022 at 11:20:06PM +0100, Christian Rauch via libcamera-devel wrote:\n> Am 30.05.22 um 11:37 schrieb Laurent Pinchart:\n> > On Mon, May 30, 2022 at 11:11:23AM +0100, Christian Rauch wrote:\n> >> Am 30.05.22 um 10:03 schrieb Laurent Pinchart:\n> >>> On Mon, May 30, 2022 at 09:47:25AM +0100, Eric Curtin via libcamera-devel wrote:\n> >>>> On Sat, 28 May 2022 at 00:19, Christian Rauch wrote:\n> >>>>> Am 22.05.22 um 11:52 schrieb Laurent Pinchart via libcamera-devel:\n> >>>>>> On Sun, May 22, 2022 at 11:08:27AM +0100, Kieran Bingham wrote:\n> >>>>>>> Quoting Laurent Pinchart (2022-05-10 14:04:57)\n> >>>>>>>> On Tue, May 10, 2022 at 01:59:37PM +0100, Eric Curtin via libcamera-devel wrote:\n> >>>>>>>>> On Tue, 10 May 2022 at 11:10, Kieran Bingham via libcamera-devel wrote:\n> >>>>>>>>>> Quoting Naushir Patuck (2022-05-10 10:49:14)\n> >>>>>>>>>>> On Tue, 10 May 2022 at 08:16, Tomi Valkeinen via libcamera-devel wrote:\n> >>>>>>\n> >>>>>> [snip]\n> >>>>>>\n> >>>>>>>>>>> For what it's worth, I absolutely loathe formatting in std iostream, and\n> >>>>>>>>>>> libfmt is a wonderful alternative.\n> >>>>>>>>>>> It also is close enough to the C++20 std::format implementation that\n> >>>>>>>>>>> eventual porting would be low effort.  So I am all for this change :)\n> >>>>>>>>\n> >>>>>>>> It's going to take a while before we can switch to C++20, but I'm\n> >>>>>>>> looking forward to it (and before that to some of the C++17 features\n> >>>>>>>> too, it would be nice to use std::optional in the public API).\n> >>>>>>>\n> >>>>>>> So, I've just seen as Christian said - we already do!\n> >>>>>>>\n> >>>>>>> It's used by color space ..\n> >>>>>>\n> >>>>>> Oops...\n> >>>>>>\n> >>>>>>> So we're faced with either reverting, or rolling back on the color-space\n> >>>>>>> change, or pushing forwards with C++17 on the public-api.\n> >>>>>>\n> >>>>>> Switching to C++17 worries me as it's fairly recent and may not be\n> >>>>>> easily available for all users. std::optional could easily rbe replaced\n> >>>>>> with a custom implementation in utils::optional if needed. I'm tempted\n> >>>>>> to leave std::optional in for now, and accept new users of std::optional\n> >>>>>> but no other C++17 APIs. Switching to utils::optional for ColorSpace\n> >>>>>> would cover new users of std::optional so we're not creating any new\n> >>>>>> liability.\n> >>>>>\n> >>>>> I think that C++17 is quite old by now (nearly 5 years) and all major\n> >>>>> compilers support it for multiple versions and years now. Do you know of\n> >>>>> an example, where requiring C++17 would hinder the adaptation of\n> >>>>> libcamera? Do any of the target system not support this standard?\n> >>>\n> >>> The main blocker used to tbe Chromium, which used C++14. It seems to\n> >>> have moved to C++17 now, and we have also dropped the plan for direct\n> >>> integration of libcamera support in Chromium (and Firefox) as they\n> >>> should go through PipeWire and the XDG camera portal.\n> >>>\n> >>>>> I think the use of std::optional to express invalid values is quite\n> >>>>> significant compared to the previous solution of using default\n> >>>>> constructed values. Reimplementing this in libcamera would be a\n> >>>>> solution, but it just adds development and maintenance costs for no\n> >>>>> obvious reason (to me).\n> >>>\n> >>> We can use std::optional (as it's there already). *If* C++17 support\n> >>> ends up being an issue in the future, requiring us to move back to\n> >>> C++14, then it will be easy to make a custom implementation, but only if\n> >>> necessary (we've done that with std::span already, which is only\n> >>> available in C++20).\n> >>>\n> >>>>>>> Maybe this deserves it's own thread...\n> >>>>>>\n> >>>>>> Done :-)\n> >>>>>>\n> >>>>>>>>> I am all in for this change also, personally I would have changed to\n> >>>>>>>>> printf for now\n> >>>>>>>>> to have one less dependency (also an easy port to C++20 std::format). The\n> >>>>>>>>> dependency list is getting large, I noticed a build of mine failing\n> >>>>>>>>> recently because\n> >>>>>>>>> I didn't have libyaml.\n> >>>>>>>>\n> >>>>>>>> I've posted a patch that falls back to a subproject in that case. Would\n> >>>>>>>> you be able to give it a try ?\n> >>>>>>>>\n> >>>>>>>>> But std::format and libfmt are quite fast and anything is better than\n> >>>>>>>>> streams so +1.\n> >>>>>>>>>\n> >>>>>>>>>> I've never used (yet) {fmt}, but I've only heard good things about\n> >>>>>>>>>> performance, and of course it's headed into the standard, so I also\n> >>>>>>>>>> think there is some good merit to be found in this development.\n> >>>>\n> >>>> Only speaking for the red hat distros, every distro that has a package\n> >>>> built for libcamera or could have in future has at least C++17,\n> >>>> including newly released LTS release RHEL9.\n> >>>\n> >>> That's good to know. The other question is whether all C++ applications\n> >>> that may want to use libcamera can be compiled with C++17 or newer.\n> >>\n> >> Isn't C++17 backwards compatible? My understanding is that every earlier\n> >> C++ standard (e.g. C++11) should be able to compile with a C++17 (or\n> >> later) compiler.\n> >\n> > Not quite I'm afraid.\n> >\n> > --------\n> > #include <map>\t/* This includes <optional> indirectly in libc++ */\n> >\n> > using namespace std;\n> >\n> > template<class T>\n> > class optional\n> > {\n> > public:\n> >         optional()\n> >         {\n> >         }\n> > };\n> >\n> > int main()\n> > {\n> >         optional<int> opt;\n> >\n> >         return 0;\n> > }\n> \n> Right, this is clashing because of ambiguous symbols from different\n> namespaces. You have a point there. I only thought in terms of API\n> compatibility there.\n> \n> Anyway, libcamera already requires C++17 and uses std::optional. I would\n> like to push for a decision if libcamera:\n> A) continues to use C++17 and std::optional\n> B) reverts those changes and implements a custom \"optional\"\n> \n> I am in favour of keeping the status quo of using std::optional. The\n> reason is that I think that not many projects (if any) will be affected\n> by these kinds of ambiguity issues above. Resorting to a custom\n> solution, just to switch to std::optional in the public API later\n> (when?), will cause more breakage issues. A custom temporary solution\n> will also require someone to implement and maintain this, knowing that\n> it will be replaced eventually anyway.\n> \n> I simply think that the benefits outweigh the potential issues. Using\n> std::optional, for example as control value return type, improves the\n> descriptiveness of the API. While on the other side, there seem to be no\n> projects struggling with the current C++17/std::optional situation.\n> \n> Sorry for pushing for a solution, but I really would like to continue\n> with my original patch set :-)\n\nWe may have trouble understanding each other. I'm fine with\nstd::optional being used in the public API for the time being. *If and\nonly if* we later realize C++17 is causing issues in some important use\ncases, then we can switch to a custom utils::optional implementation.\nThe risk of having to do so seems low to me.\n\nTl;dr: More usage of std::optional in the public API is fine.\n\n> > --------\n> >\n> > $ clang++ -W -Wall -stdlib=libc++ -std=c++14 -o version version.cpp\n> > $ clang++ -W -Wall -stdlib=libc++ -std=c++17 -o version version.cpp\n> > version.cpp:16:2: error: reference to 'optional' is ambiguous\n> >         optional<int> opt;\n> >         ^\n> > version.cpp:6:7: note: candidate found by name lookup is 'optional'\n> > class optional\n> >       ^\n> > /usr/include/c++/v1/optional:590:7: note: candidate found by name lookup is 'std::optional'\n> > class optional\n> >       ^\n> > 1 error generated.\n> >\n> >>>> Given libcamera is still 0.0 it could be a nice opportunity to take.","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 05D48BD160\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  1 Jun 2022 08:59:52 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 37B1465631;\n\tWed,  1 Jun 2022 10:59:51 +0200 (CEST)","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 BD292633A5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  1 Jun 2022 10:59:49 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(lmontsouris-659-1-41-236.w92-154.abo.wanadoo.fr [92.154.76.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 46C8F55A;\n\tWed,  1 Jun 2022 10:59:49 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1654073991;\n\tbh=rBYtHNPDhtj1EXz6p+5GvalcJX/Zz9C/25vQYJBRGXk=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=A5CaXk6s8L5L72ynwAwhrhFMK+M0XirAUD0i6HDDUIdShD2m4J4barbRpnYDbVf4A\n\twLeAwEa/QA4u0DIcxWloF5qeoygzcjw+T4pJswTd/piFUIJUcJgEQ6IztTN1k1M7ox\n\tLIwG+sdOTdMgLwD2+CfgahNSBbBrJ/C0ZD+AVXR8Oz1GYlP+Dy1dljM/UvCteyeEQ5\n\toUygqZl9AfSWRbkGeqOvgDApwbrZZo/AaJ6xoGQQXtNBLYgqfX8sujecuvxjI1WPyU\n\tbKLyCxTFGMFvhjXoqQ0EhvDws+8879Hv6aBJfVBJq3s6koFJDSH6LidEBbEjABS+ac\n\tpFM1CC6YZqLEw==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1654073989;\n\tbh=rBYtHNPDhtj1EXz6p+5GvalcJX/Zz9C/25vQYJBRGXk=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=KWdUpHP6uzxgY+XlT3NXwf8n4ohyS2Q0C0ffm+Hp7kdDLo/WU1svxMPzvJJG3DS9x\n\tjLo+mTUtKJgAd0Lk8KT4ND1Qi72107wtHTLuy1Zv5/Thx6HUJNax11UMJT6FGCzdQw\n\tb7p3mZWTT0y5+VQkcs30TcD1jsKRKtIMSS25UQaI="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"KWdUpHP6\"; dkim-atps=neutral","Date":"Wed, 1 Jun 2022 11:59:44 +0300","To":"Christian Rauch <Rauch.Christian@gmx.de>","Message-ID":"<YpcqgOzcjP0nIgco@pendragon.ideasonboard.com>","References":"<CAOgh=FzGPS9QbLG0PGArHEUwce3022PN-OEUCJ8abKthcPR86w@mail.gmail.com>\n\t<Ynpi+bfB2T+jbbWK@pendragon.ideasonboard.com>\n\t<165321410734.4128907.16683618909815271160@Monstersaurus>\n\t<YooV4xRF/KeG1CPz@pendragon.ideasonboard.com>\n\t<0f26f4cb-a28f-fef7-9e9a-d552bcbdba65@gmx.de>\n\t<CAOgh=Fw8Sug28+xhSsL70-6X8eWSvRmuLyYTSse=1iYehQPNPA@mail.gmail.com>\n\t<YpSITTmGXU5OS2kn@pendragon.ideasonboard.com>\n\t<7a240f03-2879-27cb-75ae-6e35ca6b1d9a@gmx.de>\n\t<YpSeWne89ystM6/w@pendragon.ideasonboard.com>\n\t<a751ea72-3663-be8b-2246-8b4164802b03@gmx.de>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<a751ea72-3663-be8b-2246-8b4164802b03@gmx.de>","Subject":"Re: [libcamera-devel] Usage of C++17 in the public API (was\n\t\"[PATCH] cam: convert to libfmt\")","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>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]