c++xtensor

How do I pass an XTensor array by reference so that I can modify it in-place?


I am using the C++ library XTensor. I want to modify an array in-place in a function.

void modifyXArray(xt::xarray<double>& arr) {
    arr += 1;
}

However when using this function:

auto tmp = xt::linspace(0, 1, 100);
modifyXArray(tmp);

I get the error:

/home/tom/src/xtensor-interp/tests/test_interpolator.cpp: In member function ‘virtual void Foo_Bar_Test::TestBody()’:
/home/tom/src/xtensor-interp/tests/test_interpolator.cpp:92:18: error: cannot bind non-const lvalue reference of type ‘xt::xarray<double>&’ {aka ‘xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<long unsigned int, 4, std::allocator<long unsigned int>, true>, xt::xtensor_expression_tag>&’} to an rvalue of type ‘xt::xarray<double>’ {aka ‘xt::xarray_container<xt::uvector<double, std::allocator<double> >, xt::layout_type::row_major, xt::svector<long unsigned int, 4, std::allocator<long unsigned int>, true>, xt::xtensor_expression_tag>’}
   92 |     modifyXArray(tmp);
      |                  ^~~
In file included from /home/tom/src/xtensor-interp/XTensorInterp/XTensorInterp/interpolator.h:11,
                 from /home/tom/src/xtensor-interp/tests/test_interpolator.cpp:1:
/home/tom/src/xtensor-interp/build/Release/_deps/xtensor-src/include/xtensor/xarray.hpp:501:12: note:   after user-defined conversion: ‘xt::xarray_container<EC, L, SC, Tag>::xarray_container(const xt::xexpression<E>&) [with E = xt::xgenerator<xt::detail::arange_generator<double, int, double>, int, std::array<long unsigned int, 1> >; EC = xt::uvector<double, std::allocator<double> >; xt::layout_type L = xt::layout_type::row_major; SC = xt::svector<long unsigned int, 4, std::allocator<long unsigned int>, true>; Tag = xt::xtensor_expression_tag]’
  501 |     inline xarray_container<EC, L, SC, Tag>::xarray_container(const xexpression<E>& e)
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/tom/src/xtensor-interp/tests/test_interpolator.cpp:85:39: note:   initializing argument 1 of ‘void modifyXArray(xt::xarray<double>&)’
   85 | void modifyXArray(xt::xarray<double>& arr) {
      |                   ~~~~~~~~~~~~~~~~~~~~^~~

I can pass in an array by reference if I mark it as const, but then I cant modify it.


Solution

  • tmp isn't an xt::xarray it's a builder that can automatically be converted to an array. When you pass it to modifyXArray it tries to do this conversion (see after user-defined conversion: ‘xt::xarray_container... in the error message) but the conversion returns a temporary (rvalue) and you can't bind a temporary to a non-const reference (even if you did the temporary would be thrown away and your original variable wouldn't be modified). You need to force the conversion to an array before you pass to modifyXArray, this is as simple as not using auto:

    xt::xarray<double> tmp = xt::linspace(0, 1, 100);
    modifyXArray(tmp);
    

    The conversion now happens when you construct tmp and you now have a non-temporary array that you can pass to modifyXArray