Python 向自定义C+;中/从自定义C++;使用pybind11的矩阵类 我试图用 PybDun11包装我的C++代码。在C++中,我有一个类:代码> MatRX3D 3/代码>,它作为一个三维数组(即形状为代码> [n,m,p] < /COD>)。它有以下基本特征: template <class T> class Matrix3D { public: std::vector<T> data; std::vector<size_t> shape; std::vector<size_t> strides; Matrix3D<T>(); Matrix3D<T>(std::vector<size_t>); Matrix3D<T>(const Matrix3D<T>&); T& operator() (int,int,int); }; Matrix3D<double> func ( const Matrix3D<double>& ); Matrix3D<double> func ( const Matrix3D<double>& ); Matrix3D<int > func ( const Matrix3D<int >& ); Matrix3D<double> func ( const Matrix3D<double>& ); 模板类矩阵x3d { 公众: std::矢量数据; 向量形状; 性病:病媒步幅; Matrix3D(); 矩阵3d(std::vector); 矩阵x3d(常数矩阵x3d&); 运算符()(int,int,int); };
为了最小化包装器代码,我想将这个类直接转换为NumPy数组(复制没有问题)。例如,我想直接包装具有以下签名的函数:Python 向自定义C+;中/从自定义C++;使用pybind11的矩阵类 我试图用 PybDun11包装我的C++代码。在C++中,我有一个类:代码> MatRX3D 3/代码>,它作为一个三维数组(即形状为代码> [n,m,p] < /COD>)。它有以下基本特征: template <class T> class Matrix3D { public: std::vector<T> data; std::vector<size_t> shape; std::vector<size_t> strides; Matrix3D<T>(); Matrix3D<T>(std::vector<size_t>); Matrix3D<T>(const Matrix3D<T>&); T& operator() (int,int,int); }; Matrix3D<double> func ( const Matrix3D<double>& ); Matrix3D<double> func ( const Matrix3D<double>& ); Matrix3D<int > func ( const Matrix3D<int >& ); Matrix3D<double> func ( const Matrix3D<double>& ); 模板类矩阵x3d { 公众: std::矢量数据; 向量形状; 性病:病媒步幅; Matrix3D(); 矩阵3d(std::vector); 矩阵x3d(常数矩阵x3d&); 运算符()(int,int,int); };,python,c++,arrays,numpy,pybind11,Python,C++,Arrays,Numpy,Pybind11,为了最小化包装器代码,我想将这个类直接转换为NumPy数组(复制没有问题)。例如,我想直接包装具有以下签名的函数: template <class T> class Matrix3D { public: std::vector<T> data; std::vector<size_t> shape; std::vector<size_t> strides; Matrix3D<T>(); M
template <class T> class Matrix3D
{
public:
std::vector<T> data;
std::vector<size_t> shape;
std::vector<size_t> strides;
Matrix3D<T>();
Matrix3D<T>(std::vector<size_t>);
Matrix3D<T>(const Matrix3D<T>&);
T& operator() (int,int,int);
};
Matrix3D<double> func ( const Matrix3D<double>& );
Matrix3D<double> func ( const Matrix3D<double>& );
Matrix3D<int > func ( const Matrix3D<int >& );
Matrix3D<double> func ( const Matrix3D<double>& );
Matrix3D函数(constmatrix3d&);
使用包装器代码
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
PYBIND11_PLUGIN(example) {
py::module m("example", "Module description");
m.def("func", &func, "Function description" );
return m.ptr();
}
#包括
#包括
#包括
名称空间py=pybind11;
PYBIND11_插件(示例){
模块m(“示例”、“模块描述”);
m、 def(“func”和func,“函数描述”);
返回m.ptr();
}
目前,我有另一个函数,它接受并返回py::array\t
。但我希望通过用模板替换来避免为每个函数编写包装器函数
这是为
特征库
-做的(用于阵列和(2-D)矩阵)。但是代码太复杂了,我无法从中派生出自己的代码。另外,我真的只需要包装一个简单的类。我对pybind11不熟悉,但在阅读了这个问题后,我开始感兴趣。从文件上看,你得自己写了。这显然是一个相当高级的主题,但经过一些努力似乎是可行的
从文档中剥离,这是一个转换器的外壳,用于转换C++类型<代码>
namespace pybind11 { namespace detail {
template <> struct type_caster<inty> {
public:
PYBIND11_TYPE_CASTER(inty, _("inty"));
// Conversion part 1 (Python->C++)
bool load(handle src, bool);
//Conversion part 2 (C++ -> Python)
static handle cast(inty src, return_value_policy, handle);
};
}} // namespace pybind11::detail
这看起来并不难。首先,他们确保输入类型为array\u t
(在您的情况下可能是array\u t
)。然后他们检查尺寸和一些一致性(您可能可以跳过后者)。最后创建特征矩阵。因为复制不是问题,所以此时只需创建一个新的Martix3D
实例,并用numpy数组中的数据填充它
对于l值和常量的不同情况,cast()
函数有不同的实现。我想只要在一个新的numpy数组中创建一个数据副本就足够了,如果可以的话。请参见函数如何将数组作为句柄返回
返回类型
我没有测试过任何一个,这个过程可能比看起来的要复杂得多。希望这将成为一个起点。在@kazemakase和@jagerman(后者通过)的帮助下,我已经找到了答案。类本身应该有一个构造函数,可以使用迭代器从某些输入中复制:
#include <vector>
#include <assert.h>
#include <iterator>
template <class T> class Matrix3D
{
public:
std::vector<T> data;
std::vector<size_t> shape;
std::vector<size_t> strides;
Matrix3D<T>() = default;
template<class Iterator>
Matrix3D<T>(const std::vector<size_t> &shape, Iterator first, Iterator last);
};
template <class T>
template<class Iterator>
Matrix3D<T>::Matrix3D(const std::vector<size_t> &shape_, Iterator first, Iterator last)
{
shape = shape_;
assert( shape.size() == 3 );
strides.resize(3);
strides[0] = shape[2]*shape[1];
strides[1] = shape[2];
strides[2] = 1;
int size = shape[0] * shape[1] * shape[2];
assert( last-first == size );
data.resize(size);
std::copy(first, last, data.begin());
}
#包括
#包括
#包括
模板类Matrix3D
{
公众:
std::矢量数据;
向量形状;
性病:病媒步幅;
Matrix3D()=默认值;
模板
Matrix3D(const std::vector&shape,迭代器优先,迭代器最后);
};
模板
模板
Matrix3D::Matrix3D(const std::vector&shape,迭代器优先,迭代器最后)
{
形状=形状;
断言(shape.size()==3);
步长。调整大小(3);
步幅[0]=形状[2]*形状[1];
步幅[1]=形状[2];
步幅[2]=1;
int size=shape[0]*shape[1]*shape[2];
断言(last first==大小);
数据。调整大小(大小);
std::copy(第一个,最后一个,data.begin());
}
要直接包装具有以下签名的函数:
template <class T> class Matrix3D
{
public:
std::vector<T> data;
std::vector<size_t> shape;
std::vector<size_t> strides;
Matrix3D<T>();
Matrix3D<T>(std::vector<size_t>);
Matrix3D<T>(const Matrix3D<T>&);
T& operator() (int,int,int);
};
Matrix3D<double> func ( const Matrix3D<double>& );
Matrix3D<double> func ( const Matrix3D<double>& );
Matrix3D<int > func ( const Matrix3D<int >& );
Matrix3D<double> func ( const Matrix3D<double>& );
Matrix3D函数(constmatrix3d&);
需要以下包装器代码
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
namespace pybind11 { namespace detail {
template <typename T> struct type_caster<Matrix3D<T>>
{
public:
PYBIND11_TYPE_CASTER(Matrix3D<T>, _("Matrix3D<T>"));
// Conversion part 1 (Python -> C++)
bool load(py::handle src, bool convert)
{
if ( !convert and !py::array_t<T>::check_(src) )
return false;
auto buf = py::array_t<T, py::array::c_style | py::array::forcecast>::ensure(src);
if ( !buf )
return false;
auto dims = buf.ndim();
if ( dims != 3 )
return false;
std::vector<size_t> shape(3);
for ( int i = 0 ; i < 3 ; ++i )
shape[i] = buf.shape()[i];
value = Matrix3D<T>(shape, buf.data(), buf.data()+buf.size());
return true;
}
//Conversion part 2 (C++ -> Python)
static py::handle cast(const Matrix3D<T>& src, py::return_value_policy policy, py::handle parent)
{
std::vector<size_t> shape (3);
std::vector<size_t> strides(3);
for ( int i = 0 ; i < 3 ; ++i ) {
shape [i] = src.shape [i];
strides[i] = src.strides[i]*sizeof(T);
}
py::array a(std::move(shape), std::move(strides), src.data.data() );
return a.release();
}
};
}} // namespace pybind11::detail
PYBIND11_PLUGIN(example) {
py::module m("example", "Module description");
m.def("func", &func, "Function description" );
return m.ptr();
}
#包括
#包括
#包括
名称空间py=pybind11;
命名空间pybind11{命名空间详细信息{
模板结构类型\u caster
{
公众:
PYBIND11型铸造机(Matrix3D,简称“Matrix3D”);
//转换第1部分(Python->C++)
bool加载(py::handle src,bool convert)
{
if(!convert and!py::array\u t::check(src))
返回false;
自动buf=py::array\u t::sure(src);
如果(!buf)
返回false;
自动变暗=buf.ndim();
如果(暗度!=3)
返回false;
std::矢量形状(3);
对于(int i=0;i<3;++i)
shape[i]=buf.shape()[i];
value=Matrix3D(shape,buf.data(),buf.data()+buf.size());
返回true;
}
//转换第2部分(C++->Python)
静态py::handle cast(const Matrix3D&src,py::return\u value\u policy policy,py::handle parent)
{
std::矢量形状(3);
性病:病媒步幅(3);
对于(int i=0;i<3;++i){
形状[i]=src.shape[i];
步幅[i]=src.步幅[i]*sizeof(T);
}
py::数组a(std::move(shape)、std::move(strips)、src.data.data());
返回a.release();
}
};
}}//名称空间pybind11::detail
PYBIND11_插件(示例){
模块m(“示例”、“模块描述”);
m、 def(“func”和func,“函数描述”);
返回m.ptr();
}
请注意,现在也可以进行函数重载。例如,如果存在具有以下签名的重载函数:
template <class T> class Matrix3D
{
public:
std::vector<T> data;
std::vector<size_t> shape;
std::vector<size_t> strides;
Matrix3D<T>();
Matrix3D<T>(std::vector<size_t>);
Matrix3D<T>(const Matrix3D<T>&);
T& operator() (int,int,int);
};
Matrix3D<double> func ( const Matrix3D<double>& );
Matrix3D<double> func ( const Matrix3D<double>& );
Matrix3D<int > func ( const Matrix3D<int >& );
Matrix3D<double> func ( const Matrix3D<double>& );
Matrix3D函数(constmatrix3d&);
矩阵3d函数(常数矩阵3d&);
需要以下包装器函数定义:
m.def("func", py::overload_cast<Matrix3D<int >&>(&func), "Function description" );
m.def("func", py::overload_cast<Matrix3D<double>&>(&func), "Function description" );
m.def(“func”,py::重载_cast(&func),“函数描述”);
m、 def(“func”,py::重载_cast(&func),“函数描述”);
与问题无关,但如果矩阵类具有任意形状,为什么要将其命名为Matrix3D
?是的,它应该具有任意形状(原则上,我可以使其具有任意尺寸,但对于这个特定项目,静态三维是可以的)。我想如果它有相等的维度,我可以使用来自Eigen的(实验性)张量模。我喜欢你的回答:“我不熟悉……但是……”我的观点