Я выставляю класс 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 ++.
Как я могу диагностировать это?
Рассол в порядке. Это твой код, который не так. Вы берете указатель на данные массива, ничего не делая, чтобы гарантировать, что эти данные действительно живут столько, сколько их использует объект.
Вам необходимо сохранить ссылку на массив и выполнить связанное управление подсчетом. pybind11, вероятно, имеет какой-то механизм для представления ссылки на Python и обработки пересчета для вас. Из быстрого взгляда на документы, похоже, ваш код должен array_t
по значению вместо константной ссылки (как array_t
уже представляет ссылку на Python), и сохраните его в array_t
переменная экземпляра.
Других решений пока нет …