несопоставленный доступ к памяти в массиве, переданном из Python в переполнение стека

Я выставляю класс C ++ для Python, используя pybind11,

Требуется numpy.array в своем конструкторе, и получает указатель на свои внутренние данные. (Не копирует данные).

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <iostream>

namespace py = pybind11;

struct Data
{
Data(const py::array_t<double, py::array::c_style| py::array::forcecast>& arr)
: p(arr.data())
{
std::cout << "arr=" << p    << std::endl;
std::cout << "[0]=" << p[0] << std::endl;
}
const double* p;
};

У меня есть другой класс, который принимает const Data&, тем самым получая доступ к данным массива.

struct Manager
{
Manager(const Data& data)
: data_(data)
{
const double* p = data_.p;

std::cout << "data.arr=" << p    << std::endl;
std::cout << "data.[0]=" << p[0] << std::endl;
}
const Data& data_;
};

Здесь два класса представлены Python с использованием pybind11:

PYBIND11_MODULE(foo, m)
{
py::class_<Data>(m, "Data")
.def(py::init<const py::array_t<double, py::array::c_style| py::array::forcecast>&>());

py::class_<Manager>(m, "Manager")
.def(py::init<const Data&>());
}

Это работает хорошо. Я могу импортировать свой модуль, создать Data экземпляр из numpy.array, а затем передать это Manager:

>>> import pandas
>>> import numpy
>>> import foo

>>> df = pandas.DataFrame(data = numpy.random.rand(990000, 7))
>>> d = foo.Data(df.values)
>>> c = foo.Manager(d)

Мой скрипт работает нормально, и вы можете увидеть мой код C ++, обращающийся к numpy.array данные и печать его адреса и первого элемента в стандартный вывод:

arr=0x7f47df313010
[0]=0.980507
data.arr=0x7f47df313010
data.[0]=0.980507

Все вышеперечисленное я создал в попытке создать MCVE чтобы проиллюстрировать проблему, которую я испытываю ниже.

Теперь, однако, я загружаю файл выбора pandas DataFrame, который у меня есть (вот ссылка на скачивание рассматриваемого файла рассола):

>>> import pandas
>>> import foo

>>> df = pandas.read_pickle('data5.pk')
>>> a = df.values
>>> d = foo.Data(a)
>>> c = foo.Manager(d)

и мой код C ++ падает, пытаясь получить доступ к данным массива.

Вот стандартный вывод:

arr=0x7f8864241010
arr[0]=7440.7
data.arr=0x7f8864241010
<dumps core>

Таким образом, указатель на массив одинаков в Manager, но попытка разыменования указателя вызывает SEGV.

Проходит через Valgrind, сообщает Valgrind Access not within mapped region at address 0x7f8864241010 (т.е. адрес numpy.array).

Python совершенно счастлив с моим файлом рассола:

>>> import pandas

>>> df = pandas.read_pickle('data5.pk')
>>> df.shape
(990000, 7)
>>> df
                  A             B             C            D            E  \
10000   7440.695240  15055.443905  14585.542158  3647.710616  8139.777981
10001   7440.607794  15055.356459  14585.454712  3647.623171  8139.690536
10002   7441.155761  15055.904426  14586.002679  3648.171138  8140.238503
10003   7440.430209  15055.178874  14585.277127  3647.445585  8139.512950
10004   7440.418058  15055.166724  14585.264977  3647.433435  8139.500800
10005   7440.906603  15055.655268  14585.753521  3647.921979  8139.989344
10006   7440.525167  15055.273832  14585.372085  3647.540543  8139.607908
...

Я не могу понять, что не так с моим файлом рассола.

  • Я пытался создать numpy.array и травление, это прекрасно работает
  • Я пытался создать pandas.DataFrame и травление, это прекрасно работает
  • Я нарезал свой «недействительный» фрейм данных, и я могу получить подмножество, которое отлично работает

В моих данных есть кое-что, что радует Python, но вызывает SEGV в C ++.

Как я могу диагностировать это?

0

Решение

Рассол в порядке. Это твой код, который не так. Вы берете указатель на данные массива, ничего не делая, чтобы гарантировать, что эти данные действительно живут столько, сколько их использует объект.

Вам необходимо сохранить ссылку на массив и выполнить связанное управление подсчетом. pybind11, вероятно, имеет какой-то механизм для представления ссылки на Python и обработки пересчета для вас. Из быстрого взгляда на документы, похоже, ваш код должен array_t по значению вместо константной ссылки (как array_t уже представляет ссылку на Python), и сохраните его в array_t переменная экземпляра.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]