使用pybind11将NumPy数组转换为自定义C Matrix-class

我试图使用pybind11包装我的C代码.在C中,我有一个类Matrix3D,它充当3-D阵列(即形状为[n,m,p]).它具有以下基本签名:
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);

};

为了最小化包装器代码,我想将此类直接转换为NumPy数组(副本没有问题).例如,我想直接包装以下签名的函数

Matrix3D<double> func ( const Matrix3D<double>& );

使用包装器代码

#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 :: array_t< double>的函数.但我想避免为每个函数编写一个包装函数,方法是用一些模板替换它.

这已经针对特征库(对于阵列和(2-D)矩阵)进行.但是代码对我来说太过牵扯,无法从中派生自己的代码.另外,我真的只需要包装一个简单的类.

解决方法

在@kazemakase和@jagerman(后者通过 pybind11 forum)的帮助下,我已经弄明白了.类本身应该有一个可以从一些输入复制的构造函数,这里使用迭代器:
#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 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<double> func ( const Matrix3D<double>& );

需要以下包装代码

#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","Function description" );
    return m.ptr();
}

请注意,现在也可以进行函数重载.例如,如果存在具有以下签名的重载函数

Matrix3D<int   > func ( const Matrix3D<int   >& );
Matrix3D<double> func ( const Matrix3D<double>& );

需要以下包装函数定义:

m.def("func",py::overload_cast<Matrix3D<int   >&>(&func),"Function description" );
m.def("func",py::overload_cast<Matrix3D<double>&>(&func),"Function description" );

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...