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

4

我正在尝试使用Pybind11封装一个返回STL容器的C++函数,该容器本身被包装在智能指针中。下面是一个示例。由于函数是重载的,因此我必须指定签名。

#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没有任何问题。请参见AS Mackey的答案,了解如何绑定std :: Array容器。
1个回答

7
问题可能与PYBIND11_MAKE_OPAQUE对于ArrayComplex4数据结构的处理有关,虽然我现在没有办法让它工作,但我会在有时间的时候再仔细研究一下。
以下代码是我目前为止最接近您设计的代码。我使用了一个额外的用户定义数据结构来包装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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接