From f391a20f8a643decaada611f3788235e21160382 Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Wed, 29 Apr 2026 11:02:44 +0200 Subject: [PATCH 1/5] Support streaming lazy masked expressions --- include/xtensor/core/xmath.hpp | 80 +++++++++++++++++++++++++- include/xtensor/views/xmasked_view.hpp | 26 ++++++++- test/test_xmasked_view.cpp | 75 ++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 5 deletions(-) diff --git a/include/xtensor/core/xmath.hpp b/include/xtensor/core/xmath.hpp index 77f864929..625748b64 100644 --- a/include/xtensor/core/xmath.hpp +++ b/include/xtensor/core/xmath.hpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -569,13 +570,64 @@ namespace xt namespace math { + namespace detail + { + template + constexpr decltype(auto) masked_data(const T& value) noexcept + { + return value; + } + + template + constexpr decltype(auto) masked_data(const xtl::xmasked_value& value) noexcept + { + return value.value(); + } + + template + constexpr bool masked_visible(const T&) noexcept + { + return true; + } + + template + constexpr bool masked_visible(const xtl::xmasked_value& value) noexcept + { + return static_cast(value.visible()); + } + } + template struct minimum { template constexpr auto operator()(const A1& t1, const A2& t2) const noexcept { - return xtl::select(t1 < t2, t1, t2); + if constexpr (xtl::is_xmasked_value>::value || xtl::is_xmasked_value>::value) + { + using value_type = xtl::promote_type_t< + std::decay_t, + std::decay_t>; + using return_type = xtl::xmasked_value; + + if (detail::masked_visible(t1) && detail::masked_visible(t2)) + { + return return_type( + static_cast( + detail::masked_data(t1) < detail::masked_data(t2) + ? detail::masked_data(t1) + : detail::masked_data(t2) + ), + true + ); + } + + return return_type(value_type(0), false); + } + else + { + return xtl::select(t1 < t2, t1, t2); + } } template @@ -591,7 +643,31 @@ namespace xt template constexpr auto operator()(const A1& t1, const A2& t2) const noexcept { - return xtl::select(t1 > t2, t1, t2); + if constexpr (xtl::is_xmasked_value>::value || xtl::is_xmasked_value>::value) + { + using value_type = xtl::promote_type_t< + std::decay_t, + std::decay_t>; + using return_type = xtl::xmasked_value; + + if (detail::masked_visible(t1) && detail::masked_visible(t2)) + { + return return_type( + static_cast( + detail::masked_data(t1) > detail::masked_data(t2) + ? detail::masked_data(t1) + : detail::masked_data(t2) + ), + true + ); + } + + return return_type(value_type(0), false); + } + else + { + return xtl::select(t1 > t2, t1, t2); + } } template diff --git a/include/xtensor/views/xmasked_view.hpp b/include/xtensor/views/xmasked_view.hpp index 05210d8c8..a3aca4061 100644 --- a/include/xtensor/views/xmasked_view.hpp +++ b/include/xtensor/views/xmasked_view.hpp @@ -118,14 +118,15 @@ namespace xt using bool_load_type = xtl::xmasked_value; using shape_type = typename data_type::shape_type; - using strides_type = typename data_type::strides_type; + using strides_type = xtl::mpl::eval_if_t, detail::expr_strides_type, get_strides_type>; + using backstrides_type = xtl::mpl::eval_if_t, detail::expr_backstrides_type, get_strides_type>; static constexpr layout_type static_layout = data_type::static_layout; static constexpr bool contiguous_layout = false; using inner_shape_type = typename data_type::inner_shape_type; - using inner_strides_type = typename data_type::inner_strides_type; - using inner_backstrides_type = typename data_type::inner_backstrides_type; + using inner_strides_type = xtl::mpl::eval_if_t, detail::expr_inner_strides_type, get_strides_type>; + using inner_backstrides_type = xtl::mpl::eval_if_t, detail::expr_inner_backstrides_type, get_strides_type>; using expression_tag = xtensor_expression_tag; @@ -163,7 +164,12 @@ namespace xt size_type size() const noexcept; const inner_shape_type& shape() const noexcept; + template + requires has_strides
::value const inner_strides_type& strides() const noexcept; + + template + requires has_strides
::value const inner_backstrides_type& backstrides() const noexcept; using accessible_base::dimension; using accessible_base::shape; @@ -202,6 +208,9 @@ namespace xt template bool has_linear_assign(const S& strides) const noexcept; + template + bool broadcast_shape(S& shape, bool reuse_cache = false) const; + data_type& value() noexcept; const data_type& value() const noexcept; @@ -338,6 +347,8 @@ namespace xt * Returns the strides of the xmasked_view. */ template + template + requires has_strides
::value inline auto xmasked_view::strides() const noexcept -> const inner_strides_type& { return m_data.strides(); @@ -347,6 +358,8 @@ namespace xt * Returns the backstrides of the xmasked_view. */ template + template + requires has_strides
::value inline auto xmasked_view::backstrides() const noexcept -> const inner_backstrides_type& { return m_data.backstrides(); @@ -370,6 +383,13 @@ namespace xt return false; } + template + template + inline bool xmasked_view::broadcast_shape(S& shape, bool) const + { + return xt::broadcast_shape(m_data.shape(), shape); + } + /** * Fills the data with the given value. * @param value the value to fill the data with. diff --git a/test/test_xmasked_view.cpp b/test/test_xmasked_view.cpp index 14613d169..198a24271 100644 --- a/test/test_xmasked_view.cpp +++ b/test/test_xmasked_view.cpp @@ -7,6 +7,9 @@ * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ +#include + +#include "xtensor/core/xmath.hpp" #include "xtensor/io/xio.hpp" #include "xtensor/optional/xoptional_assembly.hpp" #include "xtensor/views/xmasked_view.hpp" @@ -38,6 +41,42 @@ namespace xt return masked_view(data, std::move(mask)); } + template + struct is_masked_minimum_streamable : std::false_type + { + }; + + template + struct is_masked_minimum_streamable< + A, + B, + M, + std::void_t() + << minimum( + masked_view(std::declval(), std::declval()), + masked_view(std::declval(), std::declval()) + ))>> : std::true_type + { + }; + + template + struct is_masked_view_of_minimum_streamable : std::false_type + { + }; + + template + struct is_masked_view_of_minimum_streamable< + A, + B, + M, + std::void_t() + << masked_view(minimum(std::declval(), std::declval()), std::declval()))>> + : std::true_type + { + }; + TEST(xmasked_view, dimension) { auto data = make_test_data(); @@ -210,6 +249,27 @@ namespace xt EXPECT_EQ(data, expected2); } + TEST(xmasked_view, lazy_expression_stream) + { + using array_type = xarray; + const array_type a = {1., 1., 1., 1.}; + const array_type b = {0.1, 0.7, 0.3, 0.9}; + + const auto mask = b < 0.5; + const auto expected = eval(masked_view(minimum(a, b), mask)); + + std::stringstream expected_out; + expected_out << expected; + + std::stringstream masked_min_out; + masked_min_out << minimum(masked_view(a, mask), masked_view(b, mask)); + EXPECT_EQ(masked_min_out.str(), expected_out.str()); + + std::stringstream masked_expr_out; + masked_expr_out << masked_view(minimum(a, b), mask); + EXPECT_EQ(masked_expr_out.str(), expected_out.str()); + } + TEST(xmasked_view, assign) { xarray data = {{1., -2., 3.}, {4., 5., -6.}, {7., 8., -9.}}; @@ -223,6 +283,21 @@ namespace xt EXPECT_EQ(data, expected1); } + TEST(xmasked_view, assign_const_masked_view_rhs) + { + xarray data = {{1., -2., 3.}, {4., 5., -6.}, {7., 8., -9.}}; + const xarray data2 = {{0.1, 0.2, 0.3}, {0.4, 0.5, 0.6}, {0.7, 0.8, 0.9}}; + xarray mask = {{true, true, true}, {true, false, false}, {true, false, true}}; + + auto masked_data = masked_view(data, mask); + const auto masked_data2 = masked_view(data2, mask); + + masked_data = masked_data2; + + xarray expected = {{0.1, 0.2, 0.3}, {0.4, 5., -6.}, {0.7, 8., 0.9}}; + EXPECT_EQ(data, expected); + } + TEST(xmasked_view, view) { xt::xarray data = {{0, 1}, {2, 3}, {4, 5}}; From b0939a538267521459146206371ae02a24faf970 Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Wed, 29 Apr 2026 11:03:18 +0200 Subject: [PATCH 2/5] Formatting --- include/xtensor/core/xmath.hpp | 10 ++++------ include/xtensor/views/xmasked_view.hpp | 14 ++++++++++---- test/test_xmasked_view.cpp | 14 +++++--------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/xtensor/core/xmath.hpp b/include/xtensor/core/xmath.hpp index 625748b64..5e34e0d63 100644 --- a/include/xtensor/core/xmath.hpp +++ b/include/xtensor/core/xmath.hpp @@ -614,9 +614,8 @@ namespace xt { return return_type( static_cast( - detail::masked_data(t1) < detail::masked_data(t2) - ? detail::masked_data(t1) - : detail::masked_data(t2) + detail::masked_data(t1) < detail::masked_data(t2) ? detail::masked_data(t1) + : detail::masked_data(t2) ), true ); @@ -654,9 +653,8 @@ namespace xt { return return_type( static_cast( - detail::masked_data(t1) > detail::masked_data(t2) - ? detail::masked_data(t1) - : detail::masked_data(t2) + detail::masked_data(t1) > detail::masked_data(t2) ? detail::masked_data(t1) + : detail::masked_data(t2) ), true ); diff --git a/include/xtensor/views/xmasked_view.hpp b/include/xtensor/views/xmasked_view.hpp index a3aca4061..d5f300c1c 100644 --- a/include/xtensor/views/xmasked_view.hpp +++ b/include/xtensor/views/xmasked_view.hpp @@ -118,15 +118,21 @@ namespace xt using bool_load_type = xtl::xmasked_value; using shape_type = typename data_type::shape_type; - using strides_type = xtl::mpl::eval_if_t, detail::expr_strides_type, get_strides_type>; - using backstrides_type = xtl::mpl::eval_if_t, detail::expr_backstrides_type, get_strides_type>; + using strides_type = xtl::mpl:: + eval_if_t, detail::expr_strides_type, get_strides_type>; + using backstrides_type = xtl::mpl:: + eval_if_t, detail::expr_backstrides_type, get_strides_type>; static constexpr layout_type static_layout = data_type::static_layout; static constexpr bool contiguous_layout = false; using inner_shape_type = typename data_type::inner_shape_type; - using inner_strides_type = xtl::mpl::eval_if_t, detail::expr_inner_strides_type, get_strides_type>; - using inner_backstrides_type = xtl::mpl::eval_if_t, detail::expr_inner_backstrides_type, get_strides_type>; + using inner_strides_type = xtl::mpl:: + eval_if_t, detail::expr_inner_strides_type, get_strides_type>; + using inner_backstrides_type = xtl::mpl::eval_if_t< + has_strides, + detail::expr_inner_backstrides_type, + get_strides_type>; using expression_tag = xtensor_expression_tag; diff --git a/test/test_xmasked_view.cpp b/test/test_xmasked_view.cpp index 198a24271..a06dae585 100644 --- a/test/test_xmasked_view.cpp +++ b/test/test_xmasked_view.cpp @@ -51,12 +51,9 @@ namespace xt A, B, M, - std::void_t() - << minimum( - masked_view(std::declval(), std::declval()), - masked_view(std::declval(), std::declval()) - ))>> : std::true_type + std::void_t< + decltype(std::declval() << minimum(masked_view(std::declval(), std::declval()), masked_view(std::declval(), std::declval())))>> + : std::true_type { }; @@ -70,9 +67,8 @@ namespace xt A, B, M, - std::void_t() - << masked_view(minimum(std::declval(), std::declval()), std::declval()))>> + std::void_t< + decltype(std::declval() << masked_view(minimum(std::declval(), std::declval()), std::declval()))>> : std::true_type { }; From af83e817e57e30acb51b72618cefdf9dcabb03e6 Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:55:56 +0200 Subject: [PATCH 3/5] feat: Enhance masked view with lazy streaming support and add utility functions for stream output Co-authored-by: Copilot --- include/xtensor/core/xmath.hpp | 188 ++++++++++++++++++------- include/xtensor/views/xmasked_view.hpp | 28 ++-- test/test_utils.hpp | 15 ++ test/test_xmasked_view.cpp | 49 +------ test/test_xmath.cpp | 159 +++++++++++++++++++++ 5 files changed, 332 insertions(+), 107 deletions(-) diff --git a/include/xtensor/core/xmath.hpp b/include/xtensor/core/xmath.hpp index 5e34e0d63..13bb6c7ca 100644 --- a/include/xtensor/core/xmath.hpp +++ b/include/xtensor/core/xmath.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -595,6 +596,44 @@ namespace xt { return static_cast(value.visible()); } + + template + inline constexpr bool has_masked_value_v = (xtl::is_xmasked_value>::value || ...); + + template + using masked_data_type_t = std::decay_t()))>; + + template + using masked_common_value_type_t = xtl::promote_type_t...>; + + template + using masked_return_type_t = xtl::xmasked_value; + + template + constexpr bool all_masked_visible(const Args&... args) noexcept + { + return (masked_visible(args) && ...); + } + + template + constexpr auto hidden_masked_value() noexcept -> masked_return_type_t + { + return masked_return_type_t(T(0), false); + } + + template + constexpr auto masked_map(F&& function, const Args&... args) -> masked_return_type_t + { + if (all_masked_visible(args...)) + { + return masked_return_type_t( + static_cast(std::forward(function)(masked_data(args)...)), + true + ); + } + + return hidden_masked_value(); + } } template @@ -603,25 +642,17 @@ namespace xt template constexpr auto operator()(const A1& t1, const A2& t2) const noexcept { - if constexpr (xtl::is_xmasked_value>::value || xtl::is_xmasked_value>::value) + if constexpr (detail::has_masked_value_v) { - using value_type = xtl::promote_type_t< - std::decay_t, - std::decay_t>; - using return_type = xtl::xmasked_value; - - if (detail::masked_visible(t1) && detail::masked_visible(t2)) - { - return return_type( - static_cast( - detail::masked_data(t1) < detail::masked_data(t2) ? detail::masked_data(t1) - : detail::masked_data(t2) - ), - true - ); - } - - return return_type(value_type(0), false); + using value_type = detail::masked_common_value_type_t; + return detail::masked_map( + [](const auto& lhs, const auto& rhs) + { + return lhs < rhs ? lhs : rhs; + }, + t1, + t2 + ); } else { @@ -642,25 +673,17 @@ namespace xt template constexpr auto operator()(const A1& t1, const A2& t2) const noexcept { - if constexpr (xtl::is_xmasked_value>::value || xtl::is_xmasked_value>::value) + if constexpr (detail::has_masked_value_v) { - using value_type = xtl::promote_type_t< - std::decay_t, - std::decay_t>; - using return_type = xtl::xmasked_value; - - if (detail::masked_visible(t1) && detail::masked_visible(t2)) - { - return return_type( - static_cast( - detail::masked_data(t1) > detail::masked_data(t2) ? detail::masked_data(t1) - : detail::masked_data(t2) - ), - true - ); - } - - return return_type(value_type(0), false); + using value_type = detail::masked_common_value_type_t; + return detail::masked_map( + [](const auto& lhs, const auto& rhs) + { + return lhs > rhs ? lhs : rhs; + }, + t1, + t2 + ); } else { @@ -680,7 +703,23 @@ namespace xt template constexpr auto operator()(const A1& v, const A2& lo, const A3& hi) const { - return xtl::select(v < lo, lo, xtl::select(hi < v, hi, v)); + if constexpr (detail::has_masked_value_v) + { + using value_type = detail::masked_common_value_type_t; + return detail::masked_map( + [](const auto& value, const auto& lower, const auto& upper) + { + return value < lower ? lower : (upper < value ? upper : value); + }, + v, + lo, + hi + ); + } + else + { + return xtl::select(v < lo, lo, xtl::select(hi < v, hi, v)); + } } template @@ -692,16 +731,29 @@ namespace xt struct deg2rad { - template ::value, int> = 0> - constexpr double operator()(const A& a) const noexcept - { - return a * xt::numeric_constants::PI / 180.0; - } - - template ::value, int> = 0> + template constexpr auto operator()(const A& a) const noexcept { - return a * xt::numeric_constants::PI / A(180.0); + if constexpr (detail::has_masked_value_v) + { + using data_type = detail::masked_data_type_t; + using result_type = std::conditional_t::value, double, data_type>; + return detail::masked_map( + [](const auto& value) + { + return value * xt::numeric_constants::PI / result_type(180.0); + }, + a + ); + } + else if constexpr (xtl::is_integral::value) + { + return a * xt::numeric_constants::PI / 180.0; + } + else + { + return a * xt::numeric_constants::PI / A(180.0); + } } template ::value, int> = 0> @@ -719,16 +771,29 @@ namespace xt struct rad2deg { - template ::value, int> = 0> - constexpr double operator()(const A& a) const noexcept - { - return a * 180.0 / xt::numeric_constants::PI; - } - - template ::value, int> = 0> + template constexpr auto operator()(const A& a) const noexcept { - return a * A(180.0) / xt::numeric_constants::PI; + if constexpr (detail::has_masked_value_v) + { + using data_type = detail::masked_data_type_t; + using result_type = std::conditional_t::value, double, data_type>; + return detail::masked_map( + [](const auto& value) + { + return value * result_type(180.0) / xt::numeric_constants::PI; + }, + a + ); + } + else if constexpr (xtl::is_integral::value) + { + return a * 180.0 / xt::numeric_constants::PI; + } + else + { + return a * A(180.0) / xt::numeric_constants::PI; + } } template ::value, int> = 0> @@ -932,7 +997,22 @@ namespace xt template constexpr auto operator()(const T& x) const { - return sign_impl::run(x); + if constexpr (detail::has_masked_value_v) + { + using data_type = detail::masked_data_type_t; + using result_type = std::decay_t::run(detail::masked_data(x)))>; + return detail::masked_map( + [](const auto& value) + { + return sign_impl::run(value); + }, + x + ); + } + else + { + return sign_impl::run(x); + } } }; } diff --git a/include/xtensor/views/xmasked_view.hpp b/include/xtensor/views/xmasked_view.hpp index d5f300c1c..f2ca32d33 100644 --- a/include/xtensor/views/xmasked_view.hpp +++ b/include/xtensor/views/xmasked_view.hpp @@ -21,6 +21,19 @@ namespace xt { + namespace detail + { + template + struct xmasked_view_strides + { + using fallback_type = get_strides_type; + using strides_type = xtl::mpl::eval_if_t, expr_strides_type, fallback_type>; + using backstrides_type = xtl::mpl::eval_if_t, expr_backstrides_type, fallback_type>; + using inner_strides_type = xtl::mpl::eval_if_t, expr_inner_strides_type, fallback_type>; + using inner_backstrides_type = xtl::mpl::eval_if_t, expr_inner_backstrides_type, fallback_type>; + }; + } + /**************************** * xmasked_view declaration * *****************************/ @@ -118,21 +131,16 @@ namespace xt using bool_load_type = xtl::xmasked_value; using shape_type = typename data_type::shape_type; - using strides_type = xtl::mpl:: - eval_if_t, detail::expr_strides_type, get_strides_type>; - using backstrides_type = xtl::mpl:: - eval_if_t, detail::expr_backstrides_type, get_strides_type>; + using strides_helper = detail::xmasked_view_strides; + using strides_type = typename strides_helper::strides_type; + using backstrides_type = typename strides_helper::backstrides_type; static constexpr layout_type static_layout = data_type::static_layout; static constexpr bool contiguous_layout = false; using inner_shape_type = typename data_type::inner_shape_type; - using inner_strides_type = xtl::mpl:: - eval_if_t, detail::expr_inner_strides_type, get_strides_type>; - using inner_backstrides_type = xtl::mpl::eval_if_t< - has_strides, - detail::expr_inner_backstrides_type, - get_strides_type>; + using inner_strides_type = typename strides_helper::inner_strides_type; + using inner_backstrides_type = typename strides_helper::inner_backstrides_type; using expression_tag = xtensor_expression_tag; diff --git a/test/test_utils.hpp b/test/test_utils.hpp index d196ce63c..9855d059b 100644 --- a/test/test_utils.hpp +++ b/test/test_utils.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include "xtensor/core/xexpression.hpp" @@ -95,6 +96,20 @@ namespace xt } return res; } + + template + std::string stream_output(const E& expression) + { + std::stringstream out; + out << expression; + return out.str(); + } + + template + bool has_stream_output(const E& expression) + { + return !stream_output(expression).empty(); + } } #endif diff --git a/test/test_xmasked_view.cpp b/test/test_xmasked_view.cpp index a06dae585..382bf9261 100644 --- a/test/test_xmasked_view.cpp +++ b/test/test_xmasked_view.cpp @@ -41,38 +41,6 @@ namespace xt return masked_view(data, std::move(mask)); } - template - struct is_masked_minimum_streamable : std::false_type - { - }; - - template - struct is_masked_minimum_streamable< - A, - B, - M, - std::void_t< - decltype(std::declval() << minimum(masked_view(std::declval(), std::declval()), masked_view(std::declval(), std::declval())))>> - : std::true_type - { - }; - - template - struct is_masked_view_of_minimum_streamable : std::false_type - { - }; - - template - struct is_masked_view_of_minimum_streamable< - A, - B, - M, - std::void_t< - decltype(std::declval() << masked_view(minimum(std::declval(), std::declval()), std::declval()))>> - : std::true_type - { - }; - TEST(xmasked_view, dimension) { auto data = make_test_data(); @@ -252,18 +220,13 @@ namespace xt const array_type b = {0.1, 0.7, 0.3, 0.9}; const auto mask = b < 0.5; - const auto expected = eval(masked_view(minimum(a, b), mask)); - - std::stringstream expected_out; - expected_out << expected; - - std::stringstream masked_min_out; - masked_min_out << minimum(masked_view(a, mask), masked_view(b, mask)); - EXPECT_EQ(masked_min_out.str(), expected_out.str()); - std::stringstream masked_expr_out; - masked_expr_out << masked_view(minimum(a, b), mask); - EXPECT_EQ(masked_expr_out.str(), expected_out.str()); + EXPECT_TRUE(has_stream_output(minimum(masked_view(a, mask), masked_view(b, mask)))); + EXPECT_TRUE(has_stream_output(masked_view(minimum(a, b), mask))); + EXPECT_TRUE(has_stream_output(maximum(masked_view(a, mask), masked_view(b, mask)))); + EXPECT_TRUE(has_stream_output(masked_view(maximum(a, b), mask))); + EXPECT_TRUE(has_stream_output(clip(masked_view(a, mask), 0.2, 0.8))); + EXPECT_TRUE(has_stream_output(masked_view(clip(a, 0.2, 0.8), mask))); } TEST(xmasked_view, assign) diff --git a/test/test_xmath.cpp b/test/test_xmath.cpp index 8f56e93b9..701942339 100644 --- a/test/test_xmath.cpp +++ b/test/test_xmath.cpp @@ -14,7 +14,9 @@ #include "xtensor/containers/xarray.hpp" #include "xtensor/core/xmath.hpp" #include "xtensor/generators/xrandom.hpp" +#include "xtensor/io/xio.hpp" #include "xtensor/optional/xoptional_assembly.hpp" +#include "xtensor/views/xmasked_view.hpp" #include "test_common_macros.hpp" @@ -23,6 +25,40 @@ namespace xt using std::size_t; using shape_type = dynamic_shape; + template + void expect_streamable(const E& expression) + { + EXPECT_TRUE(has_stream_output(expression)); + } + + template + void expect_masked_unary_stream(F&& function, const D& data, const M& mask) + { + expect_streamable(function(masked_view(data, mask))); + expect_streamable(masked_view(function(data), mask)); + } + + template + void expect_masked_binary_stream(F&& function, const D1& lhs, const D2& rhs, const M& mask) + { + expect_streamable(function(masked_view(lhs, mask), masked_view(rhs, mask))); + expect_streamable(masked_view(function(lhs, rhs), mask)); + } + + template + void expect_masked_ternary_stream(F&& function, const D1& arg1, const D2& arg2, const D3& arg3, const M& mask) + { + expect_streamable(function(masked_view(arg1, mask), masked_view(arg2, mask), masked_view(arg3, mask))); + expect_streamable(masked_view(function(arg1, arg2, arg3), mask)); + } + + template + void expect_masked_ternary_scalar_stream(F&& function, const D& data, const T1& arg2, const T2& arg3, const M& mask) + { + expect_streamable(function(masked_view(data, mask), arg2, arg3)); + expect_streamable(masked_view(function(data, arg2, arg3), mask)); + } + /******************** * Basic operations * ********************/ @@ -222,6 +258,129 @@ namespace xt EXPECT_EQ(res1, clip(opt_a, 2.0, 4.0)); } + TEST(xmath, masked_view_lazy_expressions) + { + using array_type = xarray; + + const array_type a = {1., 1., 1., 1.}; + const array_type b = {0.1, 0.7, 0.3, 0.9}; + const auto mask = b < 0.5; + + const auto expected_min = eval(masked_view(minimum(a, b), mask)); + const auto expected_max = eval(masked_view(maximum(a, b), mask)); + const auto expected_clip = eval(masked_view(clip(a, 0.2, 0.8), mask)); + + EXPECT_EQ(expected_min, eval(minimum(masked_view(a, mask), masked_view(b, mask)))); + EXPECT_EQ(expected_max, eval(maximum(masked_view(a, mask), masked_view(b, mask)))); + EXPECT_EQ(expected_clip, eval(clip(masked_view(a, mask), 0.2, 0.8))); + } + + TEST(xmath, masked_view_lazy_unary_math_functions) + { + const xarray mask = {true, false, true, false}; + const xarray positive = {1.25, 1.5, 1.75, 2.0}; + const xarray unit = {-0.75, -0.25, 0.25, 0.75}; + const xarray signed_values = {-1.8, -0.2, 0.2, 1.8}; + const xarray special = { + 1.0, + std::numeric_limits::infinity(), + std::numeric_limits::quiet_NaN(), + -std::numeric_limits::infinity() + }; + + expect_masked_unary_stream([](const auto& e) { return abs(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return fabs(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return exp(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return exp2(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return expm1(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return log(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return log10(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return log2(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return log1p(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return sqrt(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return cbrt(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return sin(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return cos(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return tan(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return asin(e); }, unit, mask); + expect_masked_unary_stream([](const auto& e) { return acos(e); }, unit, mask); + expect_masked_unary_stream([](const auto& e) { return atan(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return sinh(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return cosh(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return tanh(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return asinh(e); }, signed_values, mask); + expect_masked_unary_stream([](const auto& e) { return acosh(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return atanh(e); }, unit, mask); + expect_masked_unary_stream([](const auto& e) { return erf(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return erfc(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return tgamma(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return lgamma(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return ceil(e); }, signed_values, mask); + expect_masked_unary_stream([](const auto& e) { return floor(e); }, signed_values, mask); + expect_masked_unary_stream([](const auto& e) { return trunc(e); }, signed_values, mask); + expect_masked_unary_stream([](const auto& e) { return round(e); }, signed_values, mask); + expect_masked_unary_stream([](const auto& e) { return nearbyint(e); }, signed_values, mask); + expect_masked_unary_stream([](const auto& e) { return rint(e); }, signed_values, mask); + expect_masked_unary_stream([](const auto& e) { return isfinite(e); }, special, mask); + expect_masked_unary_stream([](const auto& e) { return isinf(e); }, special, mask); + expect_masked_unary_stream([](const auto& e) { return isnan(e); }, special, mask); + expect_masked_unary_stream([](const auto& e) { return sign(e); }, signed_values, mask); + expect_masked_unary_stream([](const auto& e) { return deg2rad(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return radians(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return rad2deg(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return degrees(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return square(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return cube(e); }, positive, mask); + expect_masked_unary_stream([](const auto& e) { return pow<3>(e); }, positive, mask); + } + + TEST(xmath, masked_view_lazy_binary_math_functions) + { + const xarray mask = {true, false, true, false}; + const xarray lhs = {1.25, 1.5, 1.75, 2.0}; + const xarray rhs = {0.5, 0.75, 1.25, 1.5}; + + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return fmod(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return remainder(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return fmax(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return fmin(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return fdim(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return pow(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return hypot(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return atan2(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return minimum(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return maximum(lhs_expr, rhs_expr); }, lhs, rhs, mask); + } + + TEST(xmath, masked_view_lazy_ternary_math_functions) + { + const xarray mask = {true, false, true, false}; + const xarray a = {1.25, 1.5, 1.75, 2.0}; + const xarray b = {0.5, 0.75, 1.25, 1.5}; + const xarray c = {2.0, 2.0, 2.0, 2.0}; + + expect_masked_ternary_stream( + [](const auto& arg1_expr, const auto& arg2_expr, const auto& arg3_expr) + { + return fma(arg1_expr, arg2_expr, arg3_expr); + }, + a, + b, + c, + mask + ); + expect_masked_ternary_scalar_stream( + [](const auto& data_expr, const auto& lower, const auto& upper) + { + return clip(data_expr, lower, upper); + }, + a, + 0.75, + 1.8, + mask + ); + } + TEST(xmath, sign) { shape_type shape = {3, 2}; From 7eb537c917837f8d230ca479d01ad25d2c5ac79d Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Wed, 29 Apr 2026 18:05:53 +0200 Subject: [PATCH 4/5] formating --- test/test_xmath.cpp | 502 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 446 insertions(+), 56 deletions(-) diff --git a/test/test_xmath.cpp b/test/test_xmath.cpp index 701942339..c2e536792 100644 --- a/test/test_xmath.cpp +++ b/test/test_xmath.cpp @@ -46,14 +46,16 @@ namespace xt } template - void expect_masked_ternary_stream(F&& function, const D1& arg1, const D2& arg2, const D3& arg3, const M& mask) + void + expect_masked_ternary_stream(F&& function, const D1& arg1, const D2& arg2, const D3& arg3, const M& mask) { expect_streamable(function(masked_view(arg1, mask), masked_view(arg2, mask), masked_view(arg3, mask))); expect_streamable(masked_view(function(arg1, arg2, arg3), mask)); } template - void expect_masked_ternary_scalar_stream(F&& function, const D& data, const T1& arg2, const T2& arg3, const M& mask) + void + expect_masked_ternary_scalar_stream(F&& function, const D& data, const T1& arg2, const T2& arg3, const M& mask) { expect_streamable(function(masked_view(data, mask), arg2, arg3)); expect_streamable(masked_view(function(data, arg2, arg3), mask)); @@ -288,50 +290,358 @@ namespace xt -std::numeric_limits::infinity() }; - expect_masked_unary_stream([](const auto& e) { return abs(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return fabs(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return exp(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return exp2(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return expm1(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return log(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return log10(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return log2(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return log1p(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return sqrt(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return cbrt(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return sin(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return cos(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return tan(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return asin(e); }, unit, mask); - expect_masked_unary_stream([](const auto& e) { return acos(e); }, unit, mask); - expect_masked_unary_stream([](const auto& e) { return atan(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return sinh(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return cosh(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return tanh(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return asinh(e); }, signed_values, mask); - expect_masked_unary_stream([](const auto& e) { return acosh(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return atanh(e); }, unit, mask); - expect_masked_unary_stream([](const auto& e) { return erf(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return erfc(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return tgamma(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return lgamma(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return ceil(e); }, signed_values, mask); - expect_masked_unary_stream([](const auto& e) { return floor(e); }, signed_values, mask); - expect_masked_unary_stream([](const auto& e) { return trunc(e); }, signed_values, mask); - expect_masked_unary_stream([](const auto& e) { return round(e); }, signed_values, mask); - expect_masked_unary_stream([](const auto& e) { return nearbyint(e); }, signed_values, mask); - expect_masked_unary_stream([](const auto& e) { return rint(e); }, signed_values, mask); - expect_masked_unary_stream([](const auto& e) { return isfinite(e); }, special, mask); - expect_masked_unary_stream([](const auto& e) { return isinf(e); }, special, mask); - expect_masked_unary_stream([](const auto& e) { return isnan(e); }, special, mask); - expect_masked_unary_stream([](const auto& e) { return sign(e); }, signed_values, mask); - expect_masked_unary_stream([](const auto& e) { return deg2rad(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return radians(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return rad2deg(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return degrees(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return square(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return cube(e); }, positive, mask); - expect_masked_unary_stream([](const auto& e) { return pow<3>(e); }, positive, mask); + expect_masked_unary_stream( + [](const auto& e) + { + return abs(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return fabs(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return exp(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return exp2(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return expm1(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return log(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return log10(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return log2(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return log1p(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return sqrt(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return cbrt(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return sin(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return cos(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return tan(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return asin(e); + }, + unit, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return acos(e); + }, + unit, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return atan(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return sinh(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return cosh(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return tanh(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return asinh(e); + }, + signed_values, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return acosh(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return atanh(e); + }, + unit, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return erf(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return erfc(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return tgamma(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return lgamma(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return ceil(e); + }, + signed_values, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return floor(e); + }, + signed_values, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return trunc(e); + }, + signed_values, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return round(e); + }, + signed_values, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return nearbyint(e); + }, + signed_values, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return rint(e); + }, + signed_values, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return isfinite(e); + }, + special, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return isinf(e); + }, + special, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return isnan(e); + }, + special, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return sign(e); + }, + signed_values, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return deg2rad(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return radians(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return rad2deg(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return degrees(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return square(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return cube(e); + }, + positive, + mask + ); + expect_masked_unary_stream( + [](const auto& e) + { + return pow<3>(e); + }, + positive, + mask + ); } TEST(xmath, masked_view_lazy_binary_math_functions) @@ -340,16 +650,96 @@ namespace xt const xarray lhs = {1.25, 1.5, 1.75, 2.0}; const xarray rhs = {0.5, 0.75, 1.25, 1.5}; - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return fmod(lhs_expr, rhs_expr); }, lhs, rhs, mask); - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return remainder(lhs_expr, rhs_expr); }, lhs, rhs, mask); - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return fmax(lhs_expr, rhs_expr); }, lhs, rhs, mask); - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return fmin(lhs_expr, rhs_expr); }, lhs, rhs, mask); - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return fdim(lhs_expr, rhs_expr); }, lhs, rhs, mask); - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return pow(lhs_expr, rhs_expr); }, lhs, rhs, mask); - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return hypot(lhs_expr, rhs_expr); }, lhs, rhs, mask); - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return atan2(lhs_expr, rhs_expr); }, lhs, rhs, mask); - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return minimum(lhs_expr, rhs_expr); }, lhs, rhs, mask); - expect_masked_binary_stream([](const auto& lhs_expr, const auto& rhs_expr) { return maximum(lhs_expr, rhs_expr); }, lhs, rhs, mask); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return fmod(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return remainder(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return fmax(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return fmin(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return fdim(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return pow(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return hypot(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return atan2(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return minimum(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); + expect_masked_binary_stream( + [](const auto& lhs_expr, const auto& rhs_expr) + { + return maximum(lhs_expr, rhs_expr); + }, + lhs, + rhs, + mask + ); } TEST(xmath, masked_view_lazy_ternary_math_functions) From 76ecc5322312dc117a1bc36e720894a587a33cbb Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Thu, 30 Apr 2026 10:00:11 +0200 Subject: [PATCH 5/5] feat: Add lambda_argument and printable_value utilities for enhanced value handling Co-authored-by: Copilot --- include/xtensor/core/xmath.hpp | 37 ++++++++++++++++++++++++---------- include/xtensor/io/xio.hpp | 14 ++++++++++++- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/include/xtensor/core/xmath.hpp b/include/xtensor/core/xmath.hpp index 13bb6c7ca..e73878e11 100644 --- a/include/xtensor/core/xmath.hpp +++ b/include/xtensor/core/xmath.hpp @@ -1185,6 +1185,19 @@ namespace xt { }; + template + inline decltype(auto) lambda_argument(T&& value) + { + if constexpr (xtl::is_xmasked_value>::value) + { + return +value; + } + else + { + return std::forward(value); + } + } + template struct lambda_adapt { @@ -1194,15 +1207,15 @@ namespace xt } template - auto operator()(T... args) const + auto operator()(T&&... args) const { - return m_lambda(args...); + return m_lambda(lambda_argument(std::forward(args))...); } template )> - auto simd_apply(T... args) const + auto simd_apply(T&&... args) const { - return m_lambda(args...); + return m_lambda(lambda_argument(std::forward(args))...); } F m_lambda; @@ -1325,10 +1338,11 @@ namespace xt struct pow_impl { template - auto operator()(T v) const -> decltype(v * v) + auto operator()(T&& v) const { - T temp = pow_impl{}(v); - return temp * temp * pow_impl{}(v); + auto value = lambda_argument(std::forward(v)); + auto temp = pow_impl{}(value); + return temp * temp * pow_impl{}(value); } }; @@ -1336,9 +1350,9 @@ namespace xt struct pow_impl<1> { template - auto operator()(T v) const -> T + decltype(auto) operator()(T&& v) const { - return v; + return lambda_argument(std::forward(v)); } }; @@ -1346,9 +1360,10 @@ namespace xt struct pow_impl<0> { template - auto operator()(T /*v*/) const -> T + auto operator()(T&& v) const { - return T(1); + using value_type = std::decay_t(v)))>; + return value_type(1); } }; } diff --git a/include/xtensor/io/xio.hpp b/include/xtensor/io/xio.hpp index fbc0cd3a0..d0a99f4ed 100644 --- a/include/xtensor/io/xio.hpp +++ b/include/xtensor/io/xio.hpp @@ -185,6 +185,18 @@ namespace xt namespace detail { + template + inline auto printable_value(const xtl::xmasked_value& value) + { + return +value; + } + + template + inline const T& printable_value(const T& value) + { + return value; + } + template std::ostream& xoutput( std::ostream& out, @@ -646,7 +658,7 @@ namespace xt void update(const_reference val) { std::stringstream buf; - buf << val; + buf << printable_value(val); std::string s = buf.str(); if (int(s.size()) > m_width) {