[{"id":33657,"web_url":"https://patchwork.libcamera.org/comment/33657/","msgid":"<c796a05b-555d-4186-8e28-1101b9b317d4@ideasonboard.com>","date":"2025-03-19T16:56:20","subject":"Re: [PATCH v2 01/17] libcamera: matrix: Replace SFINAE with\n\tstatic_asserts","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"Hi\n\n\n2025. 03. 19. 17:11 keltezéssel, Stefan Klug írta:\n> SFINAE is difficult to read and not needed in these cases. Replace it\n> with static_asserts. The idea came from [1] where it is stated:\n> \n> \"The use of enable_if seems misguided to me. SFINAE is useful for the\n> situation where we consider multiple candidates for something (overloads\n> or class template specializations) and try to choose the correct one,\n> without causing compilation to fail.\"\n> \n> [1]: https://stackoverflow.com/questions/62109526/c-friend-template-that-use-sfinae\n> \n> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> \n> ---\n> \n> Changes in v2:\n> - Added this patch\n> ---\n>   include/libcamera/internal/matrix.h | 42 ++++++++---------------------\n>   1 file changed, 11 insertions(+), 31 deletions(-)\n> \n> diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h\n> index a055e6926c94..8399be583f28 100644\n> --- a/include/libcamera/internal/matrix.h\n> +++ b/include/libcamera/internal/matrix.h\n> @@ -19,14 +19,11 @@ namespace libcamera {\n>   \n>   LOG_DECLARE_CATEGORY(Matrix)\n>   \n> -#ifndef __DOXYGEN__\n> -template<typename T, unsigned int Rows, unsigned int Cols,\n> -\t std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>\n> -#else\n>   template<typename T, unsigned int Rows, unsigned int Cols>\n> -#endif /* __DOXYGEN__ */\n>   class Matrix\n>   {\n> +\tstatic_assert(std::is_arithmetic_v<T>, \"Matrix type must be arithmetic\");\n\nHere I agree with this change since there are no template specializations, etc.\nYou could also add `Rows > 0` and `Cols > 0` potentially. ANd I think the same\nshould be done with `Vector` as well.\n\n\n> +\n>   public:\n>   \tMatrix()\n>   \t{\n> @@ -78,13 +75,10 @@ public:\n>   \t\treturn Span<T, Cols>{ &data_.data()[i * Cols], Cols };\n>   \t}\n>   \n> -#ifndef __DOXYGEN__\n> -\ttemplate<typename U, std::enable_if_t<std::is_arithmetic_v<U>>>\n> -#else\n>   \ttemplate<typename U>\n> -#endif /* __DOXYGEN__ */\n> -\tMatrix<T, Rows, Cols> &operator*=(U d)\n> +\tconstexpr Matrix<T, Rows, Cols> &operator*=(U d)\n>   \t{\n> +\t\tstatic_assert(std::is_arithmetic_v<U>, \"Multiplier must be arithmetic\");\n>   \t\tfor (unsigned int i = 0; i < Rows * Cols; i++)\n>   \t\t\tdata_[i] *= d;\n>   \t\treturn *this;\n> @@ -94,14 +88,10 @@ private:\n>   \tstd::array<T, Rows * Cols> data_;\n>   };\n>   \n> -#ifndef __DOXYGEN__\n> -template<typename T, typename U, unsigned int Rows, unsigned int Cols,\n> -\t std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>\n> -#else\n>   template<typename T, typename U, unsigned int Rows, unsigned int Cols>\n> -#endif /* __DOXYGEN__ */\n> -Matrix<U, Rows, Cols> operator*(T d, const Matrix<U, Rows, Cols> &m)\n> +constexpr Matrix<U, Rows, Cols> operator*(T d, const Matrix<U, Rows, Cols> &m)\n\nBut here, where this change concerns free function operators, I am not sure,\nhere multiple overloads existing does not seem that far-fetched of an idea.\nBut since this is an internal type and no such use case exists at the moment\nI think it is fine, just wanted to mention it.\n\n\nRegards,\nBarnabás Pőcze\n\n\n>   {\n> +\tstatic_assert(std::is_arithmetic_v<T>, \"Multiplier must be arithmetic\");\n>   \tMatrix<U, Rows, Cols> result;\n>   \n>   \tfor (unsigned int i = 0; i < Rows; i++) {\n> @@ -112,27 +102,17 @@ Matrix<U, Rows, Cols> operator*(T d, const Matrix<U, Rows, Cols> &m)\n>   \treturn result;\n>   }\n>   \n> -#ifndef __DOXYGEN__\n> -template<typename T, typename U, unsigned int Rows, unsigned int Cols,\n> -\t std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>\n> -#else\n>   template<typename T, typename U, unsigned int Rows, unsigned int Cols>\n> -#endif /* __DOXYGEN__ */\n> -Matrix<U, Rows, Cols> operator*(const Matrix<U, Rows, Cols> &m, T d)\n> +constexpr Matrix<U, Rows, Cols> operator*(const Matrix<U, Rows, Cols> &m, T d)\n>   {\n> +\tstatic_assert(std::is_arithmetic_v<T>, \"Multiplier must be arithmetic\");\n>   \treturn d * m;\n>   }\n>   \n> -#ifndef __DOXYGEN__\n> -template<typename T,\n> -\t unsigned int R1, unsigned int C1,\n> -\t unsigned int R2, unsigned int C2,\n> -\t std::enable_if_t<C1 == R2> * = nullptr>\n> -#else\n> -template<typename T, unsigned int R1, unsigned int C1, unsigned int R2, unsigned in C2>\n> -#endif /* __DOXYGEN__ */\n> -Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1> &m1, const Matrix<T, R2, C2> &m2)\n> +template<typename T, unsigned int R1, unsigned int C1, unsigned int R2, unsigned int C2>\n> +constexpr Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1> &m1, const Matrix<T, R2, C2> &m2)\n>   {\n> +\tstatic_assert(C1 == R2, \"Matrix dimensions must match for multiplication\");\n>   \tMatrix<T, R1, C2> result;\n>   \n>   \tfor (unsigned int i = 0; i < R1; i++) {","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 5949DC3301\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 19 Mar 2025 16:56:28 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id ADE3B68945;\n\tWed, 19 Mar 2025 17:56:27 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 974E8687FA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Mar 2025 17:56:25 +0100 (CET)","from [192.168.33.18] (185.221.143.221.nat.pool.zt.hu\n\t[185.221.143.221])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7D68755A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 19 Mar 2025 17:54:42 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ihmyrC0M\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1742403282;\n\tbh=D/KcFHGohE912p6E+8Ih/YgXldFnVf8sDC+wD6C4WJw=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=ihmyrC0MXO/BLqiBPhdyT21N5sVGmOL8ysUiT406Av5vDY4fAnh432DTOgLNopuFi\n\tFEj22KKTaWaKjQ+1HUCOvDULa5uYHDVE+htEH3KfHXh1rgKbyYMo2UwpI2jRizNT6j\n\tj15B5k48n7oKj1mhJkGpgl+U7AIwfqvBvjmDG338=","Message-ID":"<c796a05b-555d-4186-8e28-1101b9b317d4@ideasonboard.com>","Date":"Wed, 19 Mar 2025 17:56:20 +0100","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v2 01/17] libcamera: matrix: Replace SFINAE with\n\tstatic_asserts","To":"libcamera-devel@lists.libcamera.org","References":"<20250319161152.63625-1-stefan.klug@ideasonboard.com>\n\t<20250319161152.63625-2-stefan.klug@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":"<20250319161152.63625-2-stefan.klug@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":33823,"web_url":"https://patchwork.libcamera.org/comment/33823/","msgid":"<20250401005513.GO14432@pendragon.ideasonboard.com>","date":"2025-04-01T00:55:13","subject":"Re: [PATCH v2 01/17] libcamera: matrix: Replace SFINAE with\n\tstatic_asserts","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Wed, Mar 19, 2025 at 05:56:20PM +0100, Barnabás Pőcze wrote:\n> 2025. 03. 19. 17:11 keltezéssel, Stefan Klug írta:\n> > SFINAE is difficult to read and not needed in these cases. Replace it\n> > with static_asserts. The idea came from [1] where it is stated:\n> > \n> > \"The use of enable_if seems misguided to me. SFINAE is useful for the\n> > situation where we consider multiple candidates for something (overloads\n> > or class template specializations) and try to choose the correct one,\n> > without causing compilation to fail.\"\n> > \n> > [1]: https://stackoverflow.com/questions/62109526/c-friend-template-that-use-sfinae\n> > \n> > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> > \n> > ---\n> > \n> > Changes in v2:\n> > - Added this patch\n> > ---\n> >   include/libcamera/internal/matrix.h | 42 ++++++++---------------------\n> >   1 file changed, 11 insertions(+), 31 deletions(-)\n> > \n> > diff --git a/include/libcamera/internal/matrix.h b/include/libcamera/internal/matrix.h\n> > index a055e6926c94..8399be583f28 100644\n> > --- a/include/libcamera/internal/matrix.h\n> > +++ b/include/libcamera/internal/matrix.h\n> > @@ -19,14 +19,11 @@ namespace libcamera {\n> >   \n> >   LOG_DECLARE_CATEGORY(Matrix)\n> >   \n> > -#ifndef __DOXYGEN__\n> > -template<typename T, unsigned int Rows, unsigned int Cols,\n> > -\t std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>\n> > -#else\n> >   template<typename T, unsigned int Rows, unsigned int Cols>\n> > -#endif /* __DOXYGEN__ */\n> >   class Matrix\n> >   {\n> > +\tstatic_assert(std::is_arithmetic_v<T>, \"Matrix type must be arithmetic\");\n> \n> Here I agree with this change since there are no template specializations, etc.\n> You could also add `Rows > 0` and `Cols > 0` potentially. ANd I think the same\n> should be done with `Vector` as well.\n> \n> > +\n> >   public:\n> >   \tMatrix()\n> >   \t{\n> > @@ -78,13 +75,10 @@ public:\n> >   \t\treturn Span<T, Cols>{ &data_.data()[i * Cols], Cols };\n> >   \t}\n> >   \n> > -#ifndef __DOXYGEN__\n> > -\ttemplate<typename U, std::enable_if_t<std::is_arithmetic_v<U>>>\n> > -#else\n> >   \ttemplate<typename U>\n> > -#endif /* __DOXYGEN__ */\n> > -\tMatrix<T, Rows, Cols> &operator*=(U d)\n> > +\tconstexpr Matrix<T, Rows, Cols> &operator*=(U d)\n> >   \t{\n> > +\t\tstatic_assert(std::is_arithmetic_v<U>, \"Multiplier must be arithmetic\");\n> >   \t\tfor (unsigned int i = 0; i < Rows * Cols; i++)\n> >   \t\t\tdata_[i] *= d;\n> >   \t\treturn *this;\n> > @@ -94,14 +88,10 @@ private:\n> >   \tstd::array<T, Rows * Cols> data_;\n> >   };\n> >   \n> > -#ifndef __DOXYGEN__\n> > -template<typename T, typename U, unsigned int Rows, unsigned int Cols,\n> > -\t std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>\n> > -#else\n> >   template<typename T, typename U, unsigned int Rows, unsigned int Cols>\n> > -#endif /* __DOXYGEN__ */\n> > -Matrix<U, Rows, Cols> operator*(T d, const Matrix<U, Rows, Cols> &m)\n> > +constexpr Matrix<U, Rows, Cols> operator*(T d, const Matrix<U, Rows, Cols> &m)\n> \n> But here, where this change concerns free function operators, I am not sure,\n> here multiple overloads existing does not seem that far-fetched of an idea.\n> But since this is an internal type and no such use case exists at the moment\n> I think it is fine, just wanted to mention it.\n\nSample code that exhibits the issue:\n\n--------\n#include <iostream>\n#include <type_traits>\n\ntemplate<typename T>\nT foo(T a, double b)\n{\n        static_assert(std::is_same_v<T, int>, \"Must be int\");\n        return a * b;\n}\n\ntemplate<typename T>\nT foo(T a, T b)\n{\n        return a / b;\n}\n\nint main()\n{\n        int a = foo(3, 2.0);\n        double b = foo(3.0, 2.0);\n\n        std::cout << a << \" \" << b << std::endl;\n\n        return 0;\n}\n--------\n\n$ g++ -W -Wall -o sfinae2 sfinae2.cpp \nsfinae2.cpp: In function ‘int main()’:\nsfinae2.cpp:20:23: error: call of overloaded ‘foo(double, double)’ is ambiguous\n   20 |         double b = foo(3.0, 2.0);\n      |                    ~~~^~~~~~~~~~\nsfinae2.cpp:5:3: note: candidate: ‘T foo(T, double) [with T = double]’\n    5 | T foo(T a, double b)\n      |   ^~~\nsfinae2.cpp:12:3: note: candidate: ‘T foo(T, T) [with T = double]’\n   12 | T foo(T a, T b)\n      |   ^~~\n$\n\n--------\n#include <iostream>\n#include <type_traits>\n\ntemplate<typename T, std::enable_if_t<std::is_same_v<T, int>> * = nullptr>\nT foo(T a, double b)\n{\n        return a * b;\n}\n\ntemplate<typename T>\nT foo(T a, T b)\n{\n        return a / b;\n}\n\nint main()\n{\n        int a = foo(3, 2.0);\n        double b = foo(3.0, 2.0);\n\n        std::cout << a << \" \" << b << std::endl;\n\n        return 0;\n}\n--------\n\n$ g++ -W -Wall -o sfinae2 sfinae2.cpp\n$\n\nI'd keep SFINAE for the operators. For the removal of SFINAE for the\nMatrix class itself,\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> >   {\n> > +\tstatic_assert(std::is_arithmetic_v<T>, \"Multiplier must be arithmetic\");\n> >   \tMatrix<U, Rows, Cols> result;\n> >   \n> >   \tfor (unsigned int i = 0; i < Rows; i++) {\n> > @@ -112,27 +102,17 @@ Matrix<U, Rows, Cols> operator*(T d, const Matrix<U, Rows, Cols> &m)\n> >   \treturn result;\n> >   }\n> >   \n> > -#ifndef __DOXYGEN__\n> > -template<typename T, typename U, unsigned int Rows, unsigned int Cols,\n> > -\t std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr>\n> > -#else\n> >   template<typename T, typename U, unsigned int Rows, unsigned int Cols>\n> > -#endif /* __DOXYGEN__ */\n> > -Matrix<U, Rows, Cols> operator*(const Matrix<U, Rows, Cols> &m, T d)\n> > +constexpr Matrix<U, Rows, Cols> operator*(const Matrix<U, Rows, Cols> &m, T d)\n> >   {\n> > +\tstatic_assert(std::is_arithmetic_v<T>, \"Multiplier must be arithmetic\");\n> >   \treturn d * m;\n> >   }\n> >   \n> > -#ifndef __DOXYGEN__\n> > -template<typename T,\n> > -\t unsigned int R1, unsigned int C1,\n> > -\t unsigned int R2, unsigned int C2,\n> > -\t std::enable_if_t<C1 == R2> * = nullptr>\n> > -#else\n> > -template<typename T, unsigned int R1, unsigned int C1, unsigned int R2, unsigned in C2>\n> > -#endif /* __DOXYGEN__ */\n> > -Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1> &m1, const Matrix<T, R2, C2> &m2)\n> > +template<typename T, unsigned int R1, unsigned int C1, unsigned int R2, unsigned int C2>\n> > +constexpr Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1> &m1, const Matrix<T, R2, C2> &m2)\n> >   {\n> > +\tstatic_assert(C1 == R2, \"Matrix dimensions must match for multiplication\");\n> >   \tMatrix<T, R1, C2> result;\n> >   \n> >   \tfor (unsigned int i = 0; i < R1; i++) {","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 9E090C3213\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  1 Apr 2025 00:55:40 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A4FD16897A;\n\tTue,  1 Apr 2025 02:55:39 +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 8059662C66\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  1 Apr 2025 02:55:38 +0200 (CEST)","from pendragon.ideasonboard.com (85-76-147-224-nat.elisa-mobile.fi\n\t[85.76.147.224])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 634958DB;\n\tTue,  1 Apr 2025 02:53:46 +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=\"JYctuBVh\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1743468826;\n\tbh=xA4nlTEirCobljqXhpNafyDsfx8ndFarDoMbFeotzZo=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=JYctuBVhF3QjqfQcSIlFGQdeSUsCLZ7xbUMmJuH0Pyy+djbxgRjcnNnJbBYvQzgE1\n\tpXhJZLWhz48fmFbktg5w2rAdIWlicspnQsiHu4jKileeoWSRQ3itSlo3j5e8ZZGVPw\n\tl5s7nutS1ff8MReFCi49dqLf1Cs8ysPQThIkDhGI=","Date":"Tue, 1 Apr 2025 03:55:13 +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 v2 01/17] libcamera: matrix: Replace SFINAE with\n\tstatic_asserts","Message-ID":"<20250401005513.GO14432@pendragon.ideasonboard.com>","References":"<20250319161152.63625-1-stefan.klug@ideasonboard.com>\n\t<20250319161152.63625-2-stefan.klug@ideasonboard.com>\n\t<c796a05b-555d-4186-8e28-1101b9b317d4@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<c796a05b-555d-4186-8e28-1101b9b317d4@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>"}}]