From 9196f0770f34e732a7fecf97851e311160c818ae Mon Sep 17 00:00:00 2001 From: Thomas Helfer Date: Tue, 18 Aug 2026 15:05:08 +0200 Subject: [PATCH] Fix Issue #236 --- docs/web/release-notes-3.2.md | 49 ++++++++++++++++++++--- include/MGIS/Function/Algorithms.hxx | 21 ++++++---- include/MGIS/Function/Algorithms.ixx | 17 +++++--- include/MGIS/Function/FunctionConcept.hxx | 7 ++-- include/MGIS/Function/FunctionConcept.ixx | 14 +++++-- tests/FunctionTest.cxx | 27 +++++++++++++ 6 files changed, 109 insertions(+), 26 deletions(-) diff --git a/docs/web/release-notes-3.2.md b/docs/web/release-notes-3.2.md index 05f482a78..62007fb24 100644 --- a/docs/web/release-notes-3.2.md +++ b/docs/web/release-notes-3.2.md @@ -227,17 +227,56 @@ This following code now generates a view rather than an evaluator: auto v = f.view() | as_array<2>; ~~~~ -In comparison, to achieve the same behaviour in Version 3.1, creation of -an intermediate variable was required, as follows: +> By comparison, to achieve the same behaviour in Version 3.1, creation of +> an intermediate variable was required, as follows: +> +> ~~~~{.cxx} +> // creating an array view in Version 3.1: +> auto f_view = f.view(); +> auto v = f_view | as_array<2>; +> ~~~~ + +## Temporary views can be used in "assignement" `operator|` and `assign` function + +In Version 3.1, "assignement" `operator|` and `assign` function did not +accept temporaries. + +This behaviour has been changed in Version 3.2 which allows a more +direct code: + +~~~~{.cxx} +f2 | as_scalar | (f | as_scalar); // "assignement" operator| +~~~~ + +or equivalently: ~~~~{.cxx} -// creating an array view in Version 3.1: -auto f_view = f.view(); -auto v = f_view | as_array<2>; +assign(ctx, f | as_scalar, f2 | as_scalar); ~~~~ +> By comparison, Version 3.1 would have forced to store the view resulting +> from `f | as_scalar` into a temporary as follows: +> +> ~~~~{.cxx} +> auto tmp = f | as_scalar; +> assign(ctx, tmp, f2 | as_scalar); +> ~~~~ + # Issues fixed +## Issue 236: [mgis-function] Allow "assignement" operator | and `assign` algorithm to work on temporary views + + +For more details, see + +## Issue #233: [cmake] Add a build-tests target + +For more details, see + +## Issue 232: [mgis-function] add more constraint on the `assign` algorithm to detect if values of the function can be assigned to the values of the evaluator + +For more details, see + ## Issue 227: [mgis-function] allow modifiers and views to take lightweigh functions views by copy, i.e. alleviate restrictions that views and modifiers can't operate on temporaries For more details, see diff --git a/include/MGIS/Function/Algorithms.hxx b/include/MGIS/Function/Algorithms.hxx index dcbff31dc..f10cb4f09 100644 --- a/include/MGIS/Function/Algorithms.hxx +++ b/include/MGIS/Function/Algorithms.hxx @@ -124,16 +124,18 @@ namespace mgis::function { * \param[in] e: right hand side */ template [[nodiscard]] constexpr bool assign(AbstractErrorHandler&, - FunctionType&, + FunctionType&&, const ExecutionPolicy, const EvaluatorType) // requires( - (internals::isEvaluatorAssignableToFunction)&&( - (LinearElementSpaceConcept>) || - (LinearQuadratureSpaceConcept>))); + (internals::isEvaluatorAssignableToFunction< + EvaluatorType, + std::decay_t>)&& // + ((LinearElementSpaceConcept>) || + (LinearQuadratureSpaceConcept>))); /*! * \brief assign the evaluator to a function * \param[in] ctx: execution context @@ -160,12 +162,15 @@ namespace mgis::function { * \param[in] lhs: left hand side * \param[in] e: right hand side */ - template + template [[nodiscard]] constexpr bool assign(AbstractErrorHandler&, - FunctionType&, + FunctionType&&, const EvaluatorType) // requires( - (internals::isEvaluatorAssignableToFunction)&& // + (internals::isEvaluatorAssignableToFunction< + EvaluatorType, + std::decay_t>)&& // ((LinearElementSpaceConcept>) || (LinearQuadratureSpaceConcept>))); /*! diff --git a/include/MGIS/Function/Algorithms.ixx b/include/MGIS/Function/Algorithms.ixx index 83249b074..32337ba41 100644 --- a/include/MGIS/Function/Algorithms.ixx +++ b/include/MGIS/Function/Algorithms.ixx @@ -556,14 +556,16 @@ namespace mgis::function { #ifdef MGIS_HAS_STL_PARALLEL_ALGORITHMS template constexpr bool assign(AbstractErrorHandler& ctx, - FunctionType& f, + FunctionType&& f, const ExecutionPolicy policy, const EvaluatorType e) // requires( - (internals::isEvaluatorAssignableToFunction)&& // + (internals::isEvaluatorAssignableToFunction< + EvaluatorType, + std::decay_t>)&& // ((LinearElementSpaceConcept>) || (LinearQuadratureSpaceConcept>))) { if (!areEquivalent(getSpace(f), getSpace(e))) { @@ -587,12 +589,15 @@ namespace mgis::function { #endif /* MGIS_HAS_STL_PARALLEL_ALGORITHMS */ - template + template constexpr bool assign(AbstractErrorHandler& ctx, - FunctionType& f, + FunctionType&& f, const EvaluatorType e) // requires( - (internals::isEvaluatorAssignableToFunction)&& // + (internals::isEvaluatorAssignableToFunction< + EvaluatorType, + std::decay_t>)&& // ((LinearElementSpaceConcept>) || (LinearQuadratureSpaceConcept>))) { if (!areEquivalent(getSpace(f), getSpace(e))) { diff --git a/include/MGIS/Function/FunctionConcept.hxx b/include/MGIS/Function/FunctionConcept.hxx index a94db836f..93cb302b6 100644 --- a/include/MGIS/Function/FunctionConcept.hxx +++ b/include/MGIS/Function/FunctionConcept.hxx @@ -458,10 +458,11 @@ namespace mgis::function { * \param[in] e: evaluator * \param[in] f: function */ - template - [[nodiscard]] bool operator|(EvaluatorType, FunctionType&) requires( + template + [[nodiscard]] bool operator|(EvaluatorType, FunctionType&&) requires( std::same_as, - function_space>); + function_space>>); #endif } // end of namespace mgis::function diff --git a/include/MGIS/Function/FunctionConcept.ixx b/include/MGIS/Function/FunctionConcept.ixx index 4052c8137..88366dc76 100644 --- a/include/MGIS/Function/FunctionConcept.ixx +++ b/include/MGIS/Function/FunctionConcept.ixx @@ -37,12 +37,18 @@ namespace mgis::function::internals { namespace mgis::function { - template - bool operator|(EvaluatorType e, FunctionType& f) requires( + template + bool operator|(EvaluatorType e, FunctionType&& f) requires( std::same_as, - function_space>) { + function_space>>) { Context ctx; - return assign(ctx, f, e); + if constexpr (LightweightFunctionConcept>) { + auto tmp = f; // always make a copy to allow using rvalues + return assign(ctx, tmp, e); + } else { + return assign(ctx, f, e); + } } // end of operator | } // end of namespace mgis::function diff --git a/tests/FunctionTest.cxx b/tests/FunctionTest.cxx index 2310e86f5..f5bd590b5 100644 --- a/tests/FunctionTest.cxx +++ b/tests/FunctionTest.cxx @@ -10,6 +10,7 @@ #endif #include +#include #include #include #include @@ -233,6 +234,7 @@ struct FunctionTest final : public tfel::tests::TestCase { this->test11(); this->test12(); this->test13(); + this->test14(); return this->result; } @@ -766,6 +768,31 @@ struct FunctionTest final : public tfel::tests::TestCase { TFEL_TESTS_STATIC_ASSERT(check_value(values[3], 3)); #endif /* MGIS_DISABLE_CONSTEXPR_FUNCTION_TESTS */ } // end of test13 + void test14() { +#ifndef MGIS_DISABLE_CONSTEXPR_FUNCTION_TESTS + using namespace mgis; + using namespace mgis::function; + auto check_value = [](const real& a, const real b) constexpr->bool { + constexpr auto eps = real{1e-12}; + auto local_abs = [](const real r) { return r > 0 ? r : -r; }; + return local_abs(a - b) < eps; + }; + auto ctx = Context{}; + auto space = BasicLinearSpace{2}; + auto f = Function{space, 1}; + auto f2 = Function{space, 1}; + auto f3 = Function{space, 1}; + std::tie(f(0)[0], f(1)[0]) = std::tuple{5, 12}; + const auto ok = f | as_scalar | (f2 | as_scalar); + const auto ok2 = assign(ctx, f3 | as_scalar, f | as_scalar); + TFEL_TESTS_ASSERT(ok); + TFEL_TESTS_ASSERT(ok2); + TFEL_TESTS_ASSERT(check_value(f2(0)[0], 5)); + TFEL_TESTS_ASSERT(check_value(f2(1)[0], 12)); + TFEL_TESTS_ASSERT(check_value(f3(0)[0], 5)); + TFEL_TESTS_ASSERT(check_value(f3(1)[0], 12)); +#endif /* MGIS_DISABLE_CONSTEXPR_FUNCTION_TESTS */ + } }; TFEL_TESTS_GENERATE_PROXY(FunctionTest, "FunctionTest");