Pybind11 访问不透明向量的不透明向量

Pybind11 accessing on opaque vectors of opaque vectors

为了能够在 Python 和 C++ 之间通过引用传递自定义类型的向量,我的项目在我的类型的向量上使用 PYBIND11_MAKE_OPAQUEpybind11::bind_vector<>但是,我还需要使用自定义类型的向量向量。下面是一个例子。

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <vector>

class Example {};

PYBIND11_MAKE_OPAQUE(std::vector<Example>);
PYBIND11_MAKE_OPAQUE(std::vector<std::vector<Example>>);

PYBIND11_MODULE(ExModule, m)
{
    pybind11::class_<Example>(m, "Example")
        .def(pybind11::init<>());

    pybind11::bind_vector<std::vector<Example>>(m, "ExampleVector");
    pybind11::bind_vector<std::vector<std::vector<Example>>>(m, "Example2DVector");
}

如果我在 Python 中创建我的类型的 2D 矢量,然后尝试访问它,我会收到错误消息。下面是示例 Python 代码。

from ExModule import Example, ExampleVector, Example2DVector

# a is a 10x10 vector of Examples
a = Example2DVector([ExampleVector([Example() for i in range(10)]) for i in range(10)])

b = a[4]

错误信息:

TypeError: Unable to convert function return value to a Python type! The signature was
    (self: ExModule.Example2DVector, arg0: int) -> std::vector<Example, std::allocator<Example> >

这好像是因为return在二维向量类型上的索引操作上的类型是不透明类型。我认为应该发生的是 return 应该构造成 ExampleVector。我不能从 Python 开始,它必须在模块包装器中完成。这是错误还是缺少功能?如果不是,我如何在 Example2DVector class 上重载索引运算符?

示例有效,但我的代码没有。这是因为在我的代码中,一维向量 class 在不同的 pybind11 模块 中绑定到 Python 类型 。因此,当为二维向量 class 定义 __getitem__ 时,一维向量 C++ 类型没有映射的 Python 类型。但是,我认为如果我稍后导入包含一维向量 Python 类型绑定的模块,它 应该 可以工作,但它不会。这可能是一个错误。

编辑:

这种行为不是错误(我想,Jakob 看起来是个很聪明的人)。正如 manual 中关于绑定 STL 容器的讨论,有一节是关于 "module local" 绑定的。默认情况下,类型绑定在定义它们的模块中是本地的,以避免同一类型的多个不同绑定。

但是,我们的项目包含一个 "datatypes" 模块,以及许多使用这些类型的模块。在这种情况下,我们不希望 "datatypes" 模块中定义的数据类型是模块本地的。否则,我们最终会遇到 return 值未转换为正确的 Python 类型的给定问题。

我们可以在绑定定义中关闭默认的模块本地绑定。使用问题的示例,我们可以关闭 ExampleVector 的模块本地绑定,并且访问 Example2DVector (在另一个模块中定义)将不再失败。

pybind11::bind_vector<std::vector<Example>>(m, "ExampleVector", pybind11::module_local(false));

报价单文档:

This macro must be specified at the top level (and outside of any namespaces), since it instantiates a partial template overload. If your binding code consists of multiple compilation units, it must be present in every file (typically via a common header) preceding any usage of std::vector. Opaque types must also have a corresponding class_ declaration to associate them with a name in Python, and to define a set of available operations

@ktb,这不是bug,请看https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html?highlight=compilation%20unit#making-opaque-types