使用 Pybind11 包装 STL 容器返回类型

Posted

技术标签:

【中文标题】使用 Pybind11 包装 STL 容器返回类型【英文标题】:Wrapping STL container return types using Pybind11 【发布时间】:2017-09-01 21:29:10 【问题描述】:

我想包装一个 C++ 函数(使用 Pybind11),它返回一个 STL 容器,该容器本身包装在一个智能指针中。一个例子如下所示。函数重载,所以我必须指定签名。

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include "my_class.h"

typedef std::array<std::complex<double>, 4> ArrayComplex4;
PYBIND11_MAKE_OPAQUE(ArrayComplex4);

namespace py = pybind11;
using namespace my_namespace;

PYBIND11_MODULE(my_module, m) 
    py::class_<MyClass>(m, "MyClass", py::dynamic_attr())
        .def("my_function", (std::unique_ptr<ArrayComplex4> (MyClass::*)(double)) &MyClass::my_function)
        .def("my_function", (std::unique_ptr<ArrayComplex4> (MyClass::*)(double, double)) &MyClass::my_function);

模块会编译,但是当我尝试在 Python 中使用该函数时会报错:

TypeError:无法将函数返回值转换为 Python 类型!

我确定我只是为 Pybind11 设置了错误。感谢您的帮助!

编辑

问题肯定出在我尝试绑定std::Array 数据类型的过程中。我最终修改了代码以使用std::Vector,然后 Pybind11 没有问题。有关如何绑定 std::Array 容器的信息,请参阅下面的 AS Mackey 的答案。

【问题讨论】:

【参考方案1】:

问题可能出在对 ArrayComplex4 数据结构的 PYBIND11_MAKE_OPAQUE 处理上,我无法让它工作,不过我有时间会再看一看。

以下代码,我确实开始工作了,是迄今为止我最接近您的设计的近似值。我使用了一个额外的用户定义数据结构来包装 std:: 元素:

#include <pybind11/pybind11.h>
#include <pybind11/complex.h>
#include <pybind11/stl.h>
#include <memory>
#include <complex>
#include <array>
#include <cmath>

namespace py = pybind11;


typedef std::array<std::complex<double>, 4> ArrayComplex4;

struct ArrayComplex4Holder

    ArrayComplex4 data;
    ArrayComplex4 getData()  return data; 
;

class MyClass 
public:
    MyClass()  
    std::unique_ptr<ArrayComplex4Holder> my_function(double x)
    
        std::unique_ptr<ArrayComplex4Holder> ph( new ArrayComplex4Holder());
        ph->data[0] = std::complex<double>(x);
        return ph;
    
    std::unique_ptr<ArrayComplex4Holder> my_function(double x, double y)
    
        std::unique_ptr<ArrayComplex4Holder> ph( new ArrayComplex4Holder());
        ph->data[0] = std::complex<double>(x);
        ph->data[1] = std::complex<double>(y);
        return ph;
    
;

PYBIND11_MODULE(example, m) 
    m.doc() = "pybind11 example"; // optional module docstring

    py::class_<ArrayComplex4Holder>(m, "ArrayComplex4Holder")
            .def(py::init<>())
            .def("getData", &ArrayComplex4Holder::getData);

    py::class_<MyClass>(m, "MyClass")
            .def(py::init<>())
            .def("my_function", (std::unique_ptr<ArrayComplex4Holder> (MyClass::*)(double))         &MyClass::my_function)
            .def("my_function", (std::unique_ptr<ArrayComplex4Holder> (MyClass::*)(double, double)) &MyClass::my_function);

注意,我发现需要额外的 pybind #includes 才能使 python 转换工作。

简单的python代码是:

import sys
sys.path.append('/Volumes/RAID 1/Projects/workspace/Project CPP 1')
import example

p = example.MyClass()
print (p.my_function(1.2345).getData())
print (p.my_function(1.2345, 6.7890).getData())

这会产生以下输出,看起来是正确的:

[(1.2345+0j), 0j, 0j, 0j]
[(1.2345+0j), (6.789+0j), 0j, 0j]

我希望这能给你一个工作的起点。我很想知道您是否找到更好的选择...问候,AS

【讨论】:

以上是关于使用 Pybind11 包装 STL 容器返回类型的主要内容,如果未能解决你的问题,请参考以下文章

返回多个 py::array 而不在 pybind11 中复制

在 pybind11 中使用 opencv 类型

Pybind11:外部类型作为返回值

使用 pybind11 包装 C++ 抽象类时出错

Did you forget to `#include <pybind11/stl.h>`? Or <pybind11/complex.h>,<pybind11/functional.h>

使用pybind11开发python扩展库