c++ pybind11和所有权问题

tyu7yeag  于 2023-03-09  发布在  其他
关注(0)|答案(1)|浏览(172)

我正在为一个元组类编写一个pybind11 Package 器,我希望它有__iter__方法。
为此,我将“tuple”字段转换为变量向量,并如下定义"__iter__"

cls.def(
  "__iter__", 
  [](const MyTuple& t){
      // some metaprogramming to get the underlying types of MyTuple
      using my_variant = std::variant<...>;
      std::vector<my_variant> fields; // local!
      fill(fields); // fill with t's fields converted to my_variant
      return py::make_iterator(begin(fields), end(fields));
  },
  py::keep_alive<0, 1>()
);

当然,这是一个坏主意,因为fields一旦超出范围就会死亡。
除了为MyTuple定义一个定制的保持器(它将封装这个向量)之外,是否有一个简单的技巧可以将这个辅助向量的所有权传递给pybind11?
如果我定义了一个自定义保持器,是否可以从py::class_访问它?

wrrgggsh

wrrgggsh1#

如果你的元组类可以和std::apply一起使用(或者你可以做类似的事情),你可以简单地创建一个Python元组并返回它的迭代器:

.def("__iter__", 
    [](const MyTuple& t){
        return py::iter(std::apply(
            [](auto&&... args) {
                return py::make_tuple(args...);
            },
            t));
    }) // no need for keep_alive here

你也可以创建一个自定义迭代器,例如。

template <class Tuple>
struct tuple_iterator;

template <template <typename...> typename Tuple, typename... Args>
struct tuple_iterator<Tuple<Args...>> {
    using TupleType   = Tuple<Args...>;
    using VariantType = std::variant<std::add_pointer_t<std::add_const_t<Args>>...>;

    tuple_iterator() : index_{sizeof...(Args)}, values_{} {}

    tuple_iterator(TupleType const& tuple) : index_{0} {
        std::apply(
            [this](auto const&... args) {
                values_ = {VariantType{&args}...};
            },
            tuple.v);
    }

    VariantType operator*() const { return values_[index_]; }

    tuple_iterator& operator++() {
        ++index_;
        return *this;
    }

    friend bool operator==(tuple_iterator const& lhs, tuple_iterator const& rhs) {
        return lhs.index_ == rhs.index_;
    }

private:
    std::size_t index_;
    std::vector<VariantType> values_;
};

然后您可以简单地:

.def("__iter__",
    [](MyTuple const& tuple) {
        return py::make_iterator(
            tuple_iterator<MyTuple>{tuple},
            tuple_iterator<MyTuple>{},
            py::return_value_policy::reference_internal);
    },
    py::keep_alive<0, 1>()
)

第二个版本的优点是不复制对象。

相关问题