[{"id":38689,"web_url":"https://patchwork.libcamera.org/comment/38689/","msgid":"<ff382029-85be-4d03-a683-e28086fdcd79@ideasonboard.com>","date":"2026-05-04T08:07:07","subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 05. 04. 0:53 keltezéssel, Laurent Pinchart írta:\n> This reverts commit e0e54965df015b954d75ebe945221372f2dffb80.\n> \n> utils::defopt was removed due to compilation errors with gcc 8 that were\n> deemed impossible to work around. Now that libcamera has dropped support\n> for gcc 8, restore the utility.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>   include/libcamera/base/utils.h | 14 +++++++++\n>   src/libcamera/base/utils.cpp   | 21 ++++++++++++++\n>   test/utils.cpp                 | 53 ++++++++++++++++++++++++++++++++++\n>   3 files changed, 88 insertions(+)\n> \n> diff --git a/include/libcamera/base/utils.h b/include/libcamera/base/utils.h\n> index b33a4c644a87..c962b2a3ba8e 100644\n> --- a/include/libcamera/base/utils.h\n> +++ b/include/libcamera/base/utils.h\n> @@ -430,6 +430,20 @@ scope_exit(EF) -> scope_exit<EF>;\n>   \n>   #endif /* __DOXYGEN__ */\n>   \n> +namespace details {\n> +\n> +struct defopt_t {\n> +\ttemplate<typename T>\n> +\toperator T() const\n\nconstexpr? Although arguably it probably won't get used in that context a lot.\n\n\n> +\t{\n\nPossibly\n\n   static_assert(std::is_default_constructible_v<T>);\n\ncould be added to make the error more clear.\n\n\n> +\t\treturn T{};\n> +\t}\n> +};\n> +\n> +} /* namespace details */\n> +\n> +constexpr details::defopt_t defopt;\n> +\n>   #ifndef __DOXYGEN__\n>   std::ostream &operator<<(std::ostream &os, const Duration &d);\n>   #endif\n> diff --git a/src/libcamera/base/utils.cpp b/src/libcamera/base/utils.cpp\n> index 4ab2bd863e11..7d66547cd2a5 100644\n> --- a/src/libcamera/base/utils.cpp\n> +++ b/src/libcamera/base/utils.cpp\n> @@ -685,6 +685,27 @@ void ScopeExitActions::release()\n>   \tactions_.clear();\n>   }\n>   \n> +/**\n> + * \\var defopt\n> + * \\brief Constant used with std::optional::value_or() to create a\n> + * default-constructed object\n> + *\n> + * The std::optional<T>::value_or(U &&default_value) function returns the\n> + * contained value if available, or \\a default_value if the std::optional has no\n> + * value. If the desired default value is a default-constructed T, the obvious\n> + * option is to call std::optional<T>::value_or(T{}). This approach has two\n> + * drawbacks:\n> + *\n> + * * The \\a default_value T{} is constructed even if the std::optional instance\n> + *   has a value, which impacts efficiency.\n> + * * The T{} default constructor needs to be spelled out explicitly in the\n> + *   value_or() call, leading to long lines if the type is complex.\n\nThis is not quite true anymore: https://cplusplus.github.io/LWG/issue3886\nIt's just not used in libcamera because older gcc doesn't have the change.\n\n\n> + *\n> + * The defopt variable solves these issues by providing a value that can be\n> + * passed to std::optional<T>::value_or() and get implicitly converted to a\n> + * default-constructed T.\n> + */\n> +\n>   #ifndef __DOXYGEN__\n>   std::ostream &operator<<(std::ostream &os, const Duration &d)\n>   {\n> diff --git a/test/utils.cpp b/test/utils.cpp\n> index b5ce94e5e912..eba3ed9fbf43 100644\n> --- a/test/utils.cpp\n> +++ b/test/utils.cpp\n> @@ -178,6 +178,55 @@ protected:\n>   \t\treturn TestPass;\n>   \t}\n>   \n> +\tint testDefopt()\n> +\t{\n> +\t\tstatic bool defaultConstructed = false;\n\nHmm... why not make this a member variable? Yes, I understand this is a revert,\nbut I feel like small changes like that should be ok\n\n\nReviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n\n> +\n> +\t\tstruct ValueType {\n> +\t\t\tValueType()\n> +\t\t\t\t: value_(-1)\n> +\t\t\t{\n> +\t\t\t\tdefaultConstructed = true;\n> +\t\t\t}\n> +\n> +\t\t\tValueType(int value)\n> +\t\t\t\t: value_(value)\n> +\t\t\t{\n> +\t\t\t}\n> +\n> +\t\t\tint value_;\n> +\t\t};\n> +\n> +\t\t/*\n> +\t\t * Test that utils::defopt doesn't cause default-construction\n> +\t\t * of a ValueType instance when value_or(utils::defopt) is\n> +\t\t * called on a std::optional that has a value.\n> +\t\t */\n> +\t\tstd::optional<ValueType> opt = ValueType(0);\n> +\t\tValueType value = opt.value_or(utils::defopt);\n> +\n> +\t\tif (defaultConstructed || value.value_ != 0) {\n> +\t\t\tstd::cerr << \"utils::defopt didn't prevent default construction\"\n> +\t\t\t\t  << std::endl;\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/*\n> +\t\t * Then test that the ValueType is correctly default-constructed\n> +\t\t * when the std::optional has no value.\n> +\t\t */\n> +\t\topt = std::nullopt;\n> +\t\tvalue = opt.value_or(utils::defopt);\n> +\n> +\t\tif (!defaultConstructed || value.value_ != -1) {\n> +\t\t\tstd::cerr << \"utils::defopt didn't cause default construction\"\n> +\t\t\t\t  << std::endl;\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\treturn TestPass;\n> +\t}\n> +\n>   \tint run()\n>   \t{\n>   \t\t/* utils::hex() test. */\n> @@ -332,6 +381,10 @@ protected:\n>   \t\tif (testDuration() != TestPass)\n>   \t\t\treturn TestFail;\n>   \n> +\t\t/* utils::defopt test. */\n> +\t\tif (testDefopt() != TestPass)\n> +\t\t\treturn TestFail;\n> +\n>   \t\treturn TestPass;\n>   \t}\n>   };\n> \n> base-commit: 183e37362f57ff3ce7493abf0bc6f1b57b931f55","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 B5BD0BE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 May 2026 08:07:12 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B230263025;\n\tMon,  4 May 2026 10:07:11 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A00DB6271A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 May 2026 10:07:10 +0200 (CEST)","from [192.168.33.78] (185.221.140.217.nat.pool.zt.hu\n\t[185.221.140.217])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id C54F48F;\n\tMon,  4 May 2026 10:07:08 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"wQNWFk/w\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1777882028;\n\tbh=JP7AgwpRz2yeSeXwwdf65bM1Nj2/0vat8qTkYcsFGbs=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=wQNWFk/wwK8Bk32lHabVLLbr0XsCBnqmim1CMSJbxkOpPWsmGwnVxYP3GcZ5ZkMF3\n\t06YceODgHi+dtKX49JEJ1wZQdPd/e5FoInQQosDVewnYM1Fz+SNu1hPSYGQ/x8a94u\n\t4NpZoW5bKhhLMPYVC62OKcYki4lhIhq5NlbYAHJ0=","Message-ID":"<ff382029-85be-4d03-a683-e28086fdcd79@ideasonboard.com>","Date":"Mon, 4 May 2026 10:07:07 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20260503225315.1272813-1-laurent.pinchart@ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260503225315.1272813-1-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":38699,"web_url":"https://patchwork.libcamera.org/comment/38699/","msgid":"<177788483274.45302.10478499106468788040@ping.linuxembedded.co.uk>","date":"2026-05-04T08:53:52","subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Barnabás Pőcze (2026-05-04 09:07:07)\n> 2026. 05. 04. 0:53 keltezéssel, Laurent Pinchart írta:\n> > This reverts commit e0e54965df015b954d75ebe945221372f2dffb80.\n> > \n> > utils::defopt was removed due to compilation errors with gcc 8 that were\n> > deemed impossible to work around. Now that libcamera has dropped support\n> > for gcc 8, restore the utility.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >   include/libcamera/base/utils.h | 14 +++++++++\n> >   src/libcamera/base/utils.cpp   | 21 ++++++++++++++\n> >   test/utils.cpp                 | 53 ++++++++++++++++++++++++++++++++++\n> >   3 files changed, 88 insertions(+)\n> > \n> > diff --git a/include/libcamera/base/utils.h b/include/libcamera/base/utils.h\n> > index b33a4c644a87..c962b2a3ba8e 100644\n> > --- a/include/libcamera/base/utils.h\n> > +++ b/include/libcamera/base/utils.h\n> > @@ -430,6 +430,20 @@ scope_exit(EF) -> scope_exit<EF>;\n> >   \n> >   #endif /* __DOXYGEN__ */\n> >   \n> > +namespace details {\n> > +\n> > +struct defopt_t {\n> > +     template<typename T>\n> > +     operator T() const\n> \n> constexpr? Although arguably it probably won't get used in that context a lot.\n> \n> \n> > +     {\n> \n> Possibly\n> \n>    static_assert(std::is_default_constructible_v<T>);\n> \n> could be added to make the error more clear.\n> \n> \n> > +             return T{};\n> > +     }\n> > +};\n> > +\n> > +} /* namespace details */\n> > +\n> > +constexpr details::defopt_t defopt;\n> > +\n> >   #ifndef __DOXYGEN__\n> >   std::ostream &operator<<(std::ostream &os, const Duration &d);\n> >   #endif\n> > diff --git a/src/libcamera/base/utils.cpp b/src/libcamera/base/utils.cpp\n> > index 4ab2bd863e11..7d66547cd2a5 100644\n> > --- a/src/libcamera/base/utils.cpp\n> > +++ b/src/libcamera/base/utils.cpp\n> > @@ -685,6 +685,27 @@ void ScopeExitActions::release()\n> >       actions_.clear();\n> >   }\n> >   \n> > +/**\n> > + * \\var defopt\n> > + * \\brief Constant used with std::optional::value_or() to create a\n> > + * default-constructed object\n> > + *\n> > + * The std::optional<T>::value_or(U &&default_value) function returns the\n> > + * contained value if available, or \\a default_value if the std::optional has no\n> > + * value. If the desired default value is a default-constructed T, the obvious\n> > + * option is to call std::optional<T>::value_or(T{}). This approach has two\n> > + * drawbacks:\n> > + *\n> > + * * The \\a default_value T{} is constructed even if the std::optional instance\n> > + *   has a value, which impacts efficiency.\n> > + * * The T{} default constructor needs to be spelled out explicitly in the\n> > + *   value_or() call, leading to long lines if the type is complex.\n> \n> This is not quite true anymore: https://cplusplus.github.io/LWG/issue3886\n> It's just not used in libcamera because older gcc doesn't have the change.\n> \n> \n> > + *\n> > + * The defopt variable solves these issues by providing a value that can be\n> > + * passed to std::optional<T>::value_or() and get implicitly converted to a\n> > + * default-constructed T.\n> > + */\n> > +\n> >   #ifndef __DOXYGEN__\n> >   std::ostream &operator<<(std::ostream &os, const Duration &d)\n> >   {\n> > diff --git a/test/utils.cpp b/test/utils.cpp\n> > index b5ce94e5e912..eba3ed9fbf43 100644\n> > --- a/test/utils.cpp\n> > +++ b/test/utils.cpp\n> > @@ -178,6 +178,55 @@ protected:\n> >               return TestPass;\n> >       }\n> >   \n> > +     int testDefopt()\n> > +     {\n> > +             static bool defaultConstructed = false;\n> \n> Hmm... why not make this a member variable? Yes, I understand this is a revert,\n> but I feel like small changes like that should be ok\n> \n> \n> Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n\nIt could also be a new patch if preferred rather than a revert, and\nsimply mention that it is based on the revert. Eitherway\n\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> \n> > +\n> > +             struct ValueType {\n> > +                     ValueType()\n> > +                             : value_(-1)\n> > +                     {\n> > +                             defaultConstructed = true;\n> > +                     }\n> > +\n> > +                     ValueType(int value)\n> > +                             : value_(value)\n> > +                     {\n> > +                     }\n> > +\n> > +                     int value_;\n> > +             };\n> > +\n> > +             /*\n> > +              * Test that utils::defopt doesn't cause default-construction\n> > +              * of a ValueType instance when value_or(utils::defopt) is\n> > +              * called on a std::optional that has a value.\n> > +              */\n> > +             std::optional<ValueType> opt = ValueType(0);\n> > +             ValueType value = opt.value_or(utils::defopt);\n> > +\n> > +             if (defaultConstructed || value.value_ != 0) {\n> > +                     std::cerr << \"utils::defopt didn't prevent default construction\"\n> > +                               << std::endl;\n> > +                     return TestFail;\n> > +             }\n> > +\n> > +             /*\n> > +              * Then test that the ValueType is correctly default-constructed\n> > +              * when the std::optional has no value.\n> > +              */\n> > +             opt = std::nullopt;\n> > +             value = opt.value_or(utils::defopt);\n> > +\n> > +             if (!defaultConstructed || value.value_ != -1) {\n> > +                     std::cerr << \"utils::defopt didn't cause default construction\"\n> > +                               << std::endl;\n> > +                     return TestFail;\n> > +             }\n> > +\n> > +             return TestPass;\n> > +     }\n> > +\n> >       int run()\n> >       {\n> >               /* utils::hex() test. */\n> > @@ -332,6 +381,10 @@ protected:\n> >               if (testDuration() != TestPass)\n> >                       return TestFail;\n> >   \n> > +             /* utils::defopt test. */\n> > +             if (testDefopt() != TestPass)\n> > +                     return TestFail;\n> > +\n> >               return TestPass;\n> >       }\n> >   };\n> > \n> > base-commit: 183e37362f57ff3ce7493abf0bc6f1b57b931f55\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 2E72ABDCB5\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 May 2026 08:53:57 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D3BE36301A;\n\tMon,  4 May 2026 10:53:56 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AE94C6271A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 May 2026 10:53:55 +0200 (CEST)","from monstersaurus.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id F3D78175;\n\tMon,  4 May 2026 10:53:53 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"TLaV1IoJ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1777884834;\n\tbh=KDpAqVLBKjdmSAvE73FyKS3U325pmNV55tzsk7KKRuA=;\n\th=In-Reply-To:References:Subject:From:To:Date:From;\n\tb=TLaV1IoJQELhi7b0Z4juOZGQ4kp6wRKITpleaOIGB3NLfz3aRgseS1dDBXtjVn5Xa\n\taF2H8MxJaacS4TH9sYP91n3Xs21zdDtdNkmc2RUJj2u+JPMw8TCg6ij5I5pjBIcdEj\n\tOLPNr4usRnTAW+AfFos1rh2PAX73iUKfvqiJO9FA=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<ff382029-85be-4d03-a683-e28086fdcd79@ideasonboard.com>","References":"<20260503225315.1272813-1-laurent.pinchart@ideasonboard.com>\n\t<ff382029-85be-4d03-a683-e28086fdcd79@ideasonboard.com>","Subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>, \n\tlibcamera-devel@lists.libcamera.org","Date":"Mon, 04 May 2026 09:53:52 +0100","Message-ID":"<177788483274.45302.10478499106468788040@ping.linuxembedded.co.uk>","User-Agent":"alot/0.9.1","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":38715,"web_url":"https://patchwork.libcamera.org/comment/38715/","msgid":"<20260504132949.GC1344263@killaraus.ideasonboard.com>","date":"2026-05-04T13:29:49","subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Mon, May 04, 2026 at 10:07:07AM +0200, Barnabás Pőcze wrote:\n> 2026. 05. 04. 0:53 keltezéssel, Laurent Pinchart írta:\n> > This reverts commit e0e54965df015b954d75ebe945221372f2dffb80.\n> > \n> > utils::defopt was removed due to compilation errors with gcc 8 that were\n> > deemed impossible to work around. Now that libcamera has dropped support\n> > for gcc 8, restore the utility.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >   include/libcamera/base/utils.h | 14 +++++++++\n> >   src/libcamera/base/utils.cpp   | 21 ++++++++++++++\n> >   test/utils.cpp                 | 53 ++++++++++++++++++++++++++++++++++\n> >   3 files changed, 88 insertions(+)\n> > \n> > diff --git a/include/libcamera/base/utils.h b/include/libcamera/base/utils.h\n> > index b33a4c644a87..c962b2a3ba8e 100644\n> > --- a/include/libcamera/base/utils.h\n> > +++ b/include/libcamera/base/utils.h\n> > @@ -430,6 +430,20 @@ scope_exit(EF) -> scope_exit<EF>;\n> >   \n> >   #endif /* __DOXYGEN__ */\n> >   \n> > +namespace details {\n> > +\n> > +struct defopt_t {\n> > +\ttemplate<typename T>\n> > +\toperator T() const\n> \n> constexpr? Although arguably it probably won't get used in that context a lot.\n\nShouldn't hurt.\n\n> > +\t{\n> \n> Possibly\n> \n>    static_assert(std::is_default_constructible_v<T>);\n> \n> could be added to make the error more clear.\n\nI don't know what error message the compiler would produce, but I can\ncertainly imagine a satanic chant. I'll add the static assert.\n\n> > +\t\treturn T{};\n> > +\t}\n> > +};\n> > +\n> > +} /* namespace details */\n> > +\n> > +constexpr details::defopt_t defopt;\n> > +\n> >   #ifndef __DOXYGEN__\n> >   std::ostream &operator<<(std::ostream &os, const Duration &d);\n> >   #endif\n> > diff --git a/src/libcamera/base/utils.cpp b/src/libcamera/base/utils.cpp\n> > index 4ab2bd863e11..7d66547cd2a5 100644\n> > --- a/src/libcamera/base/utils.cpp\n> > +++ b/src/libcamera/base/utils.cpp\n> > @@ -685,6 +685,27 @@ void ScopeExitActions::release()\n> >   \tactions_.clear();\n> >   }\n> >   \n> > +/**\n> > + * \\var defopt\n> > + * \\brief Constant used with std::optional::value_or() to create a\n> > + * default-constructed object\n> > + *\n> > + * The std::optional<T>::value_or(U &&default_value) function returns the\n> > + * contained value if available, or \\a default_value if the std::optional has no\n> > + * value. If the desired default value is a default-constructed T, the obvious\n> > + * option is to call std::optional<T>::value_or(T{}). This approach has two\n> > + * drawbacks:\n> > + *\n> > + * * The \\a default_value T{} is constructed even if the std::optional instance\n> > + *   has a value, which impacts efficiency.\n> > + * * The T{} default constructor needs to be spelled out explicitly in the\n> > + *   value_or() call, leading to long lines if the type is complex.\n> \n> This is not quite true anymore: https://cplusplus.github.io/LWG/issue3886\n> It's just not used in libcamera because older gcc doesn't have the change.\n\nIndeed, thanks for pointing it out. I've tested how various compiler\nversions react when using {} instead of T{}:\n\n- All gcc versions prior to 15 produce an error\n- gcc 15 compiles the code correctly\n- So do clang 20 and 21.\n\nI'll rework the documentation.\n\nA std::optional::value_or() function with no argument that\ndefault-constructs a T would have been nice.\n\n> > + *\n> > + * The defopt variable solves these issues by providing a value that can be\n> > + * passed to std::optional<T>::value_or() and get implicitly converted to a\n> > + * default-constructed T.\n> > + */\n> > +\n> >   #ifndef __DOXYGEN__\n> >   std::ostream &operator<<(std::ostream &os, const Duration &d)\n> >   {\n> > diff --git a/test/utils.cpp b/test/utils.cpp\n> > index b5ce94e5e912..eba3ed9fbf43 100644\n> > --- a/test/utils.cpp\n> > +++ b/test/utils.cpp\n> > @@ -178,6 +178,55 @@ protected:\n> >   \t\treturn TestPass;\n> >   \t}\n> >   \n> > +\tint testDefopt()\n> > +\t{\n> > +\t\tstatic bool defaultConstructed = false;\n> \n> Hmm... why not make this a member variable? Yes, I understand this is a revert,\n> but I feel like small changes like that should be ok\n\nI'll change it.\n\n> Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> \n> > +\n> > +\t\tstruct ValueType {\n> > +\t\t\tValueType()\n> > +\t\t\t\t: value_(-1)\n> > +\t\t\t{\n> > +\t\t\t\tdefaultConstructed = true;\n> > +\t\t\t}\n> > +\n> > +\t\t\tValueType(int value)\n> > +\t\t\t\t: value_(value)\n> > +\t\t\t{\n> > +\t\t\t}\n> > +\n> > +\t\t\tint value_;\n> > +\t\t};\n> > +\n> > +\t\t/*\n> > +\t\t * Test that utils::defopt doesn't cause default-construction\n> > +\t\t * of a ValueType instance when value_or(utils::defopt) is\n> > +\t\t * called on a std::optional that has a value.\n> > +\t\t */\n> > +\t\tstd::optional<ValueType> opt = ValueType(0);\n> > +\t\tValueType value = opt.value_or(utils::defopt);\n> > +\n> > +\t\tif (defaultConstructed || value.value_ != 0) {\n> > +\t\t\tstd::cerr << \"utils::defopt didn't prevent default construction\"\n> > +\t\t\t\t  << std::endl;\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/*\n> > +\t\t * Then test that the ValueType is correctly default-constructed\n> > +\t\t * when the std::optional has no value.\n> > +\t\t */\n> > +\t\topt = std::nullopt;\n> > +\t\tvalue = opt.value_or(utils::defopt);\n> > +\n> > +\t\tif (!defaultConstructed || value.value_ != -1) {\n> > +\t\t\tstd::cerr << \"utils::defopt didn't cause default construction\"\n> > +\t\t\t\t  << std::endl;\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\treturn TestPass;\n> > +\t}\n> > +\n> >   \tint run()\n> >   \t{\n> >   \t\t/* utils::hex() test. */\n> > @@ -332,6 +381,10 @@ protected:\n> >   \t\tif (testDuration() != TestPass)\n> >   \t\t\treturn TestFail;\n> >   \n> > +\t\t/* utils::defopt test. */\n> > +\t\tif (testDefopt() != TestPass)\n> > +\t\t\treturn TestFail;\n> > +\n> >   \t\treturn TestPass;\n> >   \t}\n> >   };\n> > \n> > base-commit: 183e37362f57ff3ce7493abf0bc6f1b57b931f55","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 CD8DEBE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 May 2026 13:29:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6C73D6301E;\n\tMon,  4 May 2026 15:29:53 +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 944ED62FE1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 May 2026 15:29:51 +0200 (CEST)","from killaraus.ideasonboard.com\n\t(2001-14ba-703d-e500--2a1.rev.dnainternet.fi\n\t[IPv6:2001:14ba:703d:e500::2a1])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 6A24E9C;\n\tMon,  4 May 2026 15:29:49 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"L6vpHpeU\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1777901389;\n\tbh=5vpnjNK2fOKkOgkogk2bxuGQBWedDrIRPe0eccT8Ms0=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=L6vpHpeUIViZbTIW2xx9M5O30PeBR9IJUKAAAF5K3uoVeGLySQqYW/ETuQRjL7bZW\n\tUjXsRPwFIhQIS2FlmcsC0eX5x8DmkEosAEBPBTrdINYA//KuTVgeYfLRDqLGlqU0V7\n\tAbjT394ZhViaTG4VKOssadJeGWu5nG7vZkIX47WA=","Date":"Mon, 4 May 2026 16:29:49 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","Message-ID":"<20260504132949.GC1344263@killaraus.ideasonboard.com>","References":"<20260503225315.1272813-1-laurent.pinchart@ideasonboard.com>\n\t<ff382029-85be-4d03-a683-e28086fdcd79@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<ff382029-85be-4d03-a683-e28086fdcd79@ideasonboard.com>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":38716,"web_url":"https://patchwork.libcamera.org/comment/38716/","msgid":"<20260504133942.GD1344263@killaraus.ideasonboard.com>","date":"2026-05-04T13:39:42","subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Mon, May 04, 2026 at 04:29:51PM +0300, Laurent Pinchart wrote:\n> On Mon, May 04, 2026 at 10:07:07AM +0200, Barnabás Pőcze wrote:\n> > 2026. 05. 04. 0:53 keltezéssel, Laurent Pinchart írta:\n> > > This reverts commit e0e54965df015b954d75ebe945221372f2dffb80.\n> > > \n> > > utils::defopt was removed due to compilation errors with gcc 8 that were\n> > > deemed impossible to work around. Now that libcamera has dropped support\n> > > for gcc 8, restore the utility.\n> > > \n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > >   include/libcamera/base/utils.h | 14 +++++++++\n> > >   src/libcamera/base/utils.cpp   | 21 ++++++++++++++\n> > >   test/utils.cpp                 | 53 ++++++++++++++++++++++++++++++++++\n> > >   3 files changed, 88 insertions(+)\n> > > \n> > > diff --git a/include/libcamera/base/utils.h b/include/libcamera/base/utils.h\n> > > index b33a4c644a87..c962b2a3ba8e 100644\n> > > --- a/include/libcamera/base/utils.h\n> > > +++ b/include/libcamera/base/utils.h\n> > > @@ -430,6 +430,20 @@ scope_exit(EF) -> scope_exit<EF>;\n> > >   \n> > >   #endif /* __DOXYGEN__ */\n> > >   \n> > > +namespace details {\n> > > +\n> > > +struct defopt_t {\n> > > +\ttemplate<typename T>\n> > > +\toperator T() const\n> > \n> > constexpr? Although arguably it probably won't get used in that context a lot.\n> \n> Shouldn't hurt.\n> \n> > > +\t{\n> > \n> > Possibly\n> > \n> >    static_assert(std::is_default_constructible_v<T>);\n> > \n> > could be added to make the error more clear.\n> \n> I don't know what error message the compiler would produce, but I can\n> certainly imagine a satanic chant. I'll add the static assert.\n> \n> > > +\t\treturn T{};\n> > > +\t}\n> > > +};\n> > > +\n> > > +} /* namespace details */\n> > > +\n> > > +constexpr details::defopt_t defopt;\n> > > +\n> > >   #ifndef __DOXYGEN__\n> > >   std::ostream &operator<<(std::ostream &os, const Duration &d);\n> > >   #endif\n> > > diff --git a/src/libcamera/base/utils.cpp b/src/libcamera/base/utils.cpp\n> > > index 4ab2bd863e11..7d66547cd2a5 100644\n> > > --- a/src/libcamera/base/utils.cpp\n> > > +++ b/src/libcamera/base/utils.cpp\n> > > @@ -685,6 +685,27 @@ void ScopeExitActions::release()\n> > >   \tactions_.clear();\n> > >   }\n> > >   \n> > > +/**\n> > > + * \\var defopt\n> > > + * \\brief Constant used with std::optional::value_or() to create a\n> > > + * default-constructed object\n> > > + *\n> > > + * The std::optional<T>::value_or(U &&default_value) function returns the\n> > > + * contained value if available, or \\a default_value if the std::optional has no\n> > > + * value. If the desired default value is a default-constructed T, the obvious\n> > > + * option is to call std::optional<T>::value_or(T{}). This approach has two\n> > > + * drawbacks:\n> > > + *\n> > > + * * The \\a default_value T{} is constructed even if the std::optional instance\n> > > + *   has a value, which impacts efficiency.\n> > > + * * The T{} default constructor needs to be spelled out explicitly in the\n> > > + *   value_or() call, leading to long lines if the type is complex.\n> > \n> > This is not quite true anymore: https://cplusplus.github.io/LWG/issue3886\n> > It's just not used in libcamera because older gcc doesn't have the change.\n> \n> Indeed, thanks for pointing it out. I've tested how various compiler\n> versions react when using {} instead of T{}:\n> \n> - All gcc versions prior to 15 produce an error\n> - gcc 15 compiles the code correctly\n> - So do clang 20 and 21.\n> \n> I'll rework the documentation.\n> \n> A std::optional::value_or() function with no argument that\n> default-constructs a T would have been nice.\n> \n> > > + *\n> > > + * The defopt variable solves these issues by providing a value that can be\n> > > + * passed to std::optional<T>::value_or() and get implicitly converted to a\n> > > + * default-constructed T.\n> > > + */\n> > > +\n> > >   #ifndef __DOXYGEN__\n> > >   std::ostream &operator<<(std::ostream &os, const Duration &d)\n> > >   {\n> > > diff --git a/test/utils.cpp b/test/utils.cpp\n> > > index b5ce94e5e912..eba3ed9fbf43 100644\n> > > --- a/test/utils.cpp\n> > > +++ b/test/utils.cpp\n> > > @@ -178,6 +178,55 @@ protected:\n> > >   \t\treturn TestPass;\n> > >   \t}\n> > >   \n> > > +\tint testDefopt()\n> > > +\t{\n> > > +\t\tstatic bool defaultConstructed = false;\n> > \n> > Hmm... why not make this a member variable? Yes, I understand this is a revert,\n> > but I feel like small changes like that should be ok\n> \n> I'll change it.\n\nI spoke too fast on this one. I can't access a non-static member of the\nUtilsTest class from the nested ValueType.\n\n> > Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n> > \n> > > +\n> > > +\t\tstruct ValueType {\n> > > +\t\t\tValueType()\n> > > +\t\t\t\t: value_(-1)\n> > > +\t\t\t{\n> > > +\t\t\t\tdefaultConstructed = true;\n> > > +\t\t\t}\n> > > +\n> > > +\t\t\tValueType(int value)\n> > > +\t\t\t\t: value_(value)\n> > > +\t\t\t{\n> > > +\t\t\t}\n> > > +\n> > > +\t\t\tint value_;\n> > > +\t\t};\n> > > +\n> > > +\t\t/*\n> > > +\t\t * Test that utils::defopt doesn't cause default-construction\n> > > +\t\t * of a ValueType instance when value_or(utils::defopt) is\n> > > +\t\t * called on a std::optional that has a value.\n> > > +\t\t */\n> > > +\t\tstd::optional<ValueType> opt = ValueType(0);\n> > > +\t\tValueType value = opt.value_or(utils::defopt);\n> > > +\n> > > +\t\tif (defaultConstructed || value.value_ != 0) {\n> > > +\t\t\tstd::cerr << \"utils::defopt didn't prevent default construction\"\n> > > +\t\t\t\t  << std::endl;\n> > > +\t\t\treturn TestFail;\n> > > +\t\t}\n> > > +\n> > > +\t\t/*\n> > > +\t\t * Then test that the ValueType is correctly default-constructed\n> > > +\t\t * when the std::optional has no value.\n> > > +\t\t */\n> > > +\t\topt = std::nullopt;\n> > > +\t\tvalue = opt.value_or(utils::defopt);\n> > > +\n> > > +\t\tif (!defaultConstructed || value.value_ != -1) {\n> > > +\t\t\tstd::cerr << \"utils::defopt didn't cause default construction\"\n> > > +\t\t\t\t  << std::endl;\n> > > +\t\t\treturn TestFail;\n> > > +\t\t}\n> > > +\n> > > +\t\treturn TestPass;\n> > > +\t}\n> > > +\n> > >   \tint run()\n> > >   \t{\n> > >   \t\t/* utils::hex() test. */\n> > > @@ -332,6 +381,10 @@ protected:\n> > >   \t\tif (testDuration() != TestPass)\n> > >   \t\t\treturn TestFail;\n> > >   \n> > > +\t\t/* utils::defopt test. */\n> > > +\t\tif (testDefopt() != TestPass)\n> > > +\t\t\treturn TestFail;\n> > > +\n> > >   \t\treturn TestPass;\n> > >   \t}\n> > >   };\n> > > \n> > > base-commit: 183e37362f57ff3ce7493abf0bc6f1b57b931f55","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 39E0FBDCB5\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 May 2026 13:39:46 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1EC9C6301E;\n\tMon,  4 May 2026 15:39:45 +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 4B1CE62FE1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 May 2026 15:39:44 +0200 (CEST)","from killaraus.ideasonboard.com\n\t(2001-14ba-703d-e500--2a1.rev.dnainternet.fi\n\t[IPv6:2001:14ba:703d:e500::2a1])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3A27E9C;\n\tMon,  4 May 2026 15:39:42 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"UJRA6FgE\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1777901982;\n\tbh=MdGUuyqBqXzPxNHLMtDwx+OrXjVVtJrVInLuA3TyZcA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=UJRA6FgEWl8WbCzZdHZ5PjLV6bjJcizZHC/ZsHM/DiOjzs9maMXooB3aDRktSYlgT\n\tBUDAy5/qwz6SCbhEfJ8ghaftdVBsP7ln9+aW4c6PRmZTcrUcm+V9aGMXgjQfaw+GVT\n\tctX4jFYSJRCPYo1RTg0HT5hTIlqoCyrfM78Tijbc=","Date":"Mon, 4 May 2026 16:39:42 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","Message-ID":"<20260504133942.GD1344263@killaraus.ideasonboard.com>","References":"<20260503225315.1272813-1-laurent.pinchart@ideasonboard.com>\n\t<ff382029-85be-4d03-a683-e28086fdcd79@ideasonboard.com>\n\t<20260504132949.GC1344263@killaraus.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20260504132949.GC1344263@killaraus.ideasonboard.com>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":38717,"web_url":"https://patchwork.libcamera.org/comment/38717/","msgid":"<55d9bd59-c83e-420d-8845-52432a2160dd@ideasonboard.com>","date":"2026-05-04T14:17:48","subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"2026. 05. 04. 15:39 keltezéssel, Laurent Pinchart írta:\n> On Mon, May 04, 2026 at 04:29:51PM +0300, Laurent Pinchart wrote:\n>> On Mon, May 04, 2026 at 10:07:07AM +0200, Barnabás Pőcze wrote:\n>>> 2026. 05. 04. 0:53 keltezéssel, Laurent Pinchart írta:\n>>>> This reverts commit e0e54965df015b954d75ebe945221372f2dffb80.\n>>>>\n>>>> utils::defopt was removed due to compilation errors with gcc 8 that were\n>>>> deemed impossible to work around. Now that libcamera has dropped support\n>>>> for gcc 8, restore the utility.\n>>>>\n>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n>>>> ---\n>>>>    include/libcamera/base/utils.h | 14 +++++++++\n>>>>    src/libcamera/base/utils.cpp   | 21 ++++++++++++++\n>>>>    test/utils.cpp                 | 53 ++++++++++++++++++++++++++++++++++\n>>>>    3 files changed, 88 insertions(+)\n>>>>\n>>>> diff --git a/include/libcamera/base/utils.h b/include/libcamera/base/utils.h\n>>>> index b33a4c644a87..c962b2a3ba8e 100644\n>>>> --- a/include/libcamera/base/utils.h\n>>>> +++ b/include/libcamera/base/utils.h\n>>>> @@ -430,6 +430,20 @@ scope_exit(EF) -> scope_exit<EF>;\n>>>>    \n>>>>    #endif /* __DOXYGEN__ */\n>>>>    \n>>>> +namespace details {\n>>>> +\n>>>> +struct defopt_t {\n>>>> +\ttemplate<typename T>\n>>>> +\toperator T() const\n>>>\n>>> constexpr? Although arguably it probably won't get used in that context a lot.\n>>\n>> Shouldn't hurt.\n>>\n>>>> +\t{\n>>>\n>>> Possibly\n>>>\n>>>     static_assert(std::is_default_constructible_v<T>);\n>>>\n>>> could be added to make the error more clear.\n>>\n>> I don't know what error message the compiler would produce, but I can\n>> certainly imagine a satanic chant. I'll add the static assert.\n>>\n>>>> +\t\treturn T{};\n>>>> +\t}\n>>>> +};\n>>>> +\n>>>> +} /* namespace details */\n>>>> +\n>>>> +constexpr details::defopt_t defopt;\n>>>> +\n>>>>    #ifndef __DOXYGEN__\n>>>>    std::ostream &operator<<(std::ostream &os, const Duration &d);\n>>>>    #endif\n>>>> diff --git a/src/libcamera/base/utils.cpp b/src/libcamera/base/utils.cpp\n>>>> index 4ab2bd863e11..7d66547cd2a5 100644\n>>>> --- a/src/libcamera/base/utils.cpp\n>>>> +++ b/src/libcamera/base/utils.cpp\n>>>> @@ -685,6 +685,27 @@ void ScopeExitActions::release()\n>>>>    \tactions_.clear();\n>>>>    }\n>>>>    \n>>>> +/**\n>>>> + * \\var defopt\n>>>> + * \\brief Constant used with std::optional::value_or() to create a\n>>>> + * default-constructed object\n>>>> + *\n>>>> + * The std::optional<T>::value_or(U &&default_value) function returns the\n>>>> + * contained value if available, or \\a default_value if the std::optional has no\n>>>> + * value. If the desired default value is a default-constructed T, the obvious\n>>>> + * option is to call std::optional<T>::value_or(T{}). This approach has two\n>>>> + * drawbacks:\n>>>> + *\n>>>> + * * The \\a default_value T{} is constructed even if the std::optional instance\n>>>> + *   has a value, which impacts efficiency.\n>>>> + * * The T{} default constructor needs to be spelled out explicitly in the\n>>>> + *   value_or() call, leading to long lines if the type is complex.\n>>>\n>>> This is not quite true anymore: https://cplusplus.github.io/LWG/issue3886\n>>> It's just not used in libcamera because older gcc doesn't have the change.\n>>\n>> Indeed, thanks for pointing it out. I've tested how various compiler\n>> versions react when using {} instead of T{}:\n>>\n>> - All gcc versions prior to 15 produce an error\n>> - gcc 15 compiles the code correctly\n>> - So do clang 20 and 21.\n>>\n>> I'll rework the documentation.\n>>\n>> A std::optional::value_or() function with no argument that\n>> default-constructs a T would have been nice.\n>>\n>>>> + *\n>>>> + * The defopt variable solves these issues by providing a value that can be\n>>>> + * passed to std::optional<T>::value_or() and get implicitly converted to a\n>>>> + * default-constructed T.\n>>>> + */\n>>>> +\n>>>>    #ifndef __DOXYGEN__\n>>>>    std::ostream &operator<<(std::ostream &os, const Duration &d)\n>>>>    {\n>>>> diff --git a/test/utils.cpp b/test/utils.cpp\n>>>> index b5ce94e5e912..eba3ed9fbf43 100644\n>>>> --- a/test/utils.cpp\n>>>> +++ b/test/utils.cpp\n>>>> @@ -178,6 +178,55 @@ protected:\n>>>>    \t\treturn TestPass;\n>>>>    \t}\n>>>>    \n>>>> +\tint testDefopt()\n>>>> +\t{\n>>>> +\t\tstatic bool defaultConstructed = false;\n>>>\n>>> Hmm... why not make this a member variable? Yes, I understand this is a revert,\n>>> but I feel like small changes like that should be ok\n>>\n>> I'll change it.\n> \n> I spoke too fast on this one. I can't access a non-static member of the\n> UtilsTest class from the nested ValueType.\n\nAhh, I meant making it a member of `ValueType`. It's fine anyway.\n\n\n> \n>>> Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n>>>\n>>>> +\n>>>> +\t\tstruct ValueType {\n>>>> +\t\t\tValueType()\n>>>> +\t\t\t\t: value_(-1)\n>>>> +\t\t\t{\n>>>> +\t\t\t\tdefaultConstructed = true;\n>>>> +\t\t\t}\n>>>> +\n>>>> +\t\t\tValueType(int value)\n>>>> +\t\t\t\t: value_(value)\n>>>> +\t\t\t{\n>>>> +\t\t\t}\n>>>> +\n>>>> +\t\t\tint value_;\n>>>> +\t\t};\n>>>> +\n>>>> +\t\t/*\n>>>> +\t\t * Test that utils::defopt doesn't cause default-construction\n>>>> +\t\t * of a ValueType instance when value_or(utils::defopt) is\n>>>> +\t\t * called on a std::optional that has a value.\n>>>> +\t\t */\n>>>> +\t\tstd::optional<ValueType> opt = ValueType(0);\n>>>> +\t\tValueType value = opt.value_or(utils::defopt);\n>>>> +\n>>>> +\t\tif (defaultConstructed || value.value_ != 0) {\n>>>> +\t\t\tstd::cerr << \"utils::defopt didn't prevent default construction\"\n>>>> +\t\t\t\t  << std::endl;\n>>>> +\t\t\treturn TestFail;\n>>>> +\t\t}\n>>>> +\n>>>> +\t\t/*\n>>>> +\t\t * Then test that the ValueType is correctly default-constructed\n>>>> +\t\t * when the std::optional has no value.\n>>>> +\t\t */\n>>>> +\t\topt = std::nullopt;\n>>>> +\t\tvalue = opt.value_or(utils::defopt);\n>>>> +\n>>>> +\t\tif (!defaultConstructed || value.value_ != -1) {\n>>>> +\t\t\tstd::cerr << \"utils::defopt didn't cause default construction\"\n>>>> +\t\t\t\t  << std::endl;\n>>>> +\t\t\treturn TestFail;\n>>>> +\t\t}\n>>>> +\n>>>> +\t\treturn TestPass;\n>>>> +\t}\n>>>> +\n>>>>    \tint run()\n>>>>    \t{\n>>>>    \t\t/* utils::hex() test. */\n>>>> @@ -332,6 +381,10 @@ protected:\n>>>>    \t\tif (testDuration() != TestPass)\n>>>>    \t\t\treturn TestFail;\n>>>>    \n>>>> +\t\t/* utils::defopt test. */\n>>>> +\t\tif (testDefopt() != TestPass)\n>>>> +\t\t\treturn TestFail;\n>>>> +\n>>>>    \t\treturn TestPass;\n>>>>    \t}\n>>>>    };\n>>>>\n>>>> base-commit: 183e37362f57ff3ce7493abf0bc6f1b57b931f55\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 A3D52BDCB5\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 May 2026 14:17:54 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5F95D6301E;\n\tMon,  4 May 2026 16:17:53 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 11E0662FE1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 May 2026 16:17:52 +0200 (CEST)","from [192.168.33.78] (185.221.140.217.nat.pool.zt.hu\n\t[185.221.140.217])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0B5F39C;\n\tMon,  4 May 2026 16:17:50 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ZoyTde1T\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1777904270;\n\tbh=FUKxpKFHmScuJ/TZWj8Yyc8CVBlHvUVMcL07oi30Pec=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=ZoyTde1TrCB7lkglIbuiYCoV83xdAaY8KJZmFMcbgnpgJgzkLfHtV17NwiCc/jWCT\n\tosgzgVd6Hyp1esC9CrOvxoc1C+BBHgpNDVgMTGWgPwfQwAIZIyEvcTw6rjS2BrkKOO\n\t9P7ET2ZEBCdJeR7OD9mTraBFkNQe2rQBajDPwlhc=","Message-ID":"<55d9bd59-c83e-420d-8845-52432a2160dd@ideasonboard.com>","Date":"Mon, 4 May 2026 16:17:48 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 1/2] Revert \"libcamera: base: utils: Drop defopt\"","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","References":"<20260503225315.1272813-1-laurent.pinchart@ideasonboard.com>\n\t<ff382029-85be-4d03-a683-e28086fdcd79@ideasonboard.com>\n\t<20260504132949.GC1344263@killaraus.ideasonboard.com>\n\t<20260504133942.GD1344263@killaraus.ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20260504133942.GD1344263@killaraus.ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]