Увидев пример xtensor
матричных адаптеров 1D-контейнеров, мне стало интересно, можно ли также иметь адаптер изменяемого размера для STL-контейнеров, таких как векторы.
В процессе хотелось бы еще и узнать и использовать тип такого адаптера, а не использовать auto
.
Начиная с адаптера массива в стиле C и функции compute()
, которая умножает вектор-строку 2x1 на столбец 2x1 и возвращает xarray в виде матрицы 2x2, я добрался до
#include <cstddef>
#include <xtensor/xarray.hpp>
#include <xtensor/xadapt.hpp>
template <class A>
void compute ( A& a ) {
xt::xarray<double> b {1., 2.};
b.reshape({2, 1});
a = a * b; // size has changed, shape is now { 2, 2 }
}
int main() {
std::size_t size = 2;
////////////////////////////////////////////////////////////////
double* datavx = new double [ size ];
for ( unsigned i = 0; i < size; i++ )
datavx [ i ] = i;
std::vector<std::size_t> shapevx = { size };
std::vector<std::size_t> stridesvx = { 1 };
auto vx = xt::adapt ( datavx, size, xt::acquire_ownership(), shapevx, stridesvx );
std::cout << "\nbefore reshape\n";
for ( unsigned i = 0; i < size; i++ )
std::cout << datavx [ i ] << " ";
compute ( vx );
std::cout << "\nafter reshape\n";
for ( unsigned i = 0; i < size * size; i++ )
std::cout << datavx [ i ] << " ";
////////////////////////////////////////////////////////////////
std::vector<double> datav (size);
for (unsigned i = 0; i < size; i++)
datav[i] = i;
std::vector<std::size_t> shapev = shapevx;
std::vector<std::size_t> stridesv = stridesvx;
using shape_type = std::vector<size_t>;
using adaptor_typev = xt::xarray_adaptor<xt::xbuffer_adaptor<double*, xt::acquire_ownership>, xt::layout_type::dynamic, shape_type >;
adaptor_typev my_adaptorv = xt::adapt(datav.data(), size, xt::acquire_ownership(), shapev, stridesv);
std::cout << "\n";
std::cout << "\nbefore reshape\n";
for (unsigned i = 0; i < size; i++)
std::cout << datav[i] << " ";
compute(my_adaptorv);
std::cout << "\nafter reshape\n";
for (unsigned i = 0; i < size * size; i++)
std::cout << datav[i] << " ";
std::cout << std::endl;
// prints 0 1 0 2
}
Результат, который я получаю от xarray_adaptor в стиле C (с веб-сайта xtensor) и std:vector xarray_adaptor соответственно:
before reshape
0 1
after reshape
0 1 0 2
before reshape
0 1
after reshape
0 1 nan -5.11336e-311
==== Program exited with exit code: 3221226356 ====
Time elapsed: 000:00.016 (MM:SS.MS)
Последние два значения предполагают, что изменения размера не произошло, а ненулевой код выхода показывает, что все не так. Если я запущу его в отладчике, он выйдет с SIGTRAP в конце main()
. В верхней части стека вызовов написано ntdll!RtllsZeroMemory
.
Означает ли это, что для изменения размера адаптера xarray
для std::vector
мне нужно отдельно изменить размер вектора? Или есть способ сделать это, не пытаясь стать владельцем данных вектора (начнем с того, что это звучит неправильно)?
Во втором цикле vdata[i]
демонстрирует неопределенное поведение путем доступа к индексу за пределами границ. vdata.size()
по-прежнему равно 2, действительные индексы — 0 и 1.
Покажите минимальный воспроизводимый пример, который фактически воспроизводит показанный результат. Вы говорите о «std:vector xarray_adaptor», но в вашем коде ничего подобного не появляется.
В вашем минимальном воспроизводимом примере отсутствует определение функции compute()
(она не является частью библиотеки xtensor). Код в вопросах должен быть самодостаточным, не полагаясь на внешние ссылки (не говоря уже о том, что в этой ссылке есть три функции compute()
, что заставляет нас вручную устранять перегрузку).
«Начиная с адаптера массива C-стиля» — Почему? Если вы перейдете по своей ссылке, а затем прокрутите вверх (та же страница), там будет «Адаптация std::vector». Почему бы вам не начать с «Адаптации std::vector», если ваша цель — адаптировать std::vector
?
Извините @IgorTandetnik, пример теперь полная программа. Итак, двойное удаление (одно из адаптера и одно из вектора) приводит к сбою программы? Еще один момент, который вы отмечаете по поводу того, что resize()
не происходит, заключается в том, что право собственности на вектор не меняется?
Спасибо @JaMiT — код «Адаптация std::vector», безусловно, самый простой, но две вещи, которые я упоминаю в своем посте: использование указанного типа, а не автора, и изменение размера контейнера после адаптации, не входят в этот пример.
Как я уже сказал, вы используете адаптер для массивов в стиле C для адаптации необработанных данных, управляемых вектором. И вы сообщаете этому адаптеру, что он должен стать владельцем данных, в то время как вектор также владеет теми же данными. Вектор понятия не имеет, что вы вытаскиваете его данные прямо из-под его ног. Часть кода, которая касается datav
и my_adaptorv
, имеет множество неопределенного поведения. Посмотрите, изменится ли результат, если вы это сделаете auto my_adaptorv = xt::adapt(datav, shapev, stridesv);
Комментарии @JaMit и @IgorTandetnik помогли мне в правильном направлении - мне действительно нужен был адаптер вектора, а не массива в стиле C.
Но во всех примерах с векторными адаптерами использовались объявления auto
, и мне все равно хотелось узнать тип адаптера.
старый ответ на StackOverflow позволил мне объединить эти два варианта: этот фрагмент кода показывает тип адаптера, который я затем использовал для объявления «вручную».
xt::adapt(vdata.data(), vsize, xt::acquire_ownership(), ...)
в конечном итоге приведет к неопределенному поведению путем двойного разрушения. Вы сообщаете адаптеру, что теперь он владеет данными и должен их удалить, но векторvdata
также считает, что данные принадлежат ему, и также удалит их.