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+;中/从自定义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

为了最小化包装器代码,我想将这个类直接转换为NumPy数组(复制没有问题)。例如,我想直接包装具有以下签名的函数:

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的(实验性)张量模。我喜欢你的回答:“我不熟悉……但是……”我的观点