英文:
pybind11 and ownership issues
问题
我正在为一个类编写 pybind11 的包装器,类似于元组,我希望它有一个 __iter__
方法。
为了做到这一点,我将“元组”字段转换为变体的向量,并像这样定义“iter”:
cls.def(
"__iter__",
[](const MyTuple& t){
// 一些元编程来获取 MyTuple 的基础类型
using my_variant = std::variant<...>;
std::vector<my_variant> fields; // 本地变量!
fill(fields); // 用 t 的字段填充,转换为 my_variant
return py::make_iterator(begin(fields), end(fields));
},
py::keep_alive<0, 1>()
);
当然,这是一个坏主意,因为 fields
一旦超出范围就会消失。
除了为 MyTuple
定义一个自定义持有者(封装这个向量),是否有一种简单的技巧可以将这个辅助向量的所有权传递给 pybind11?
如果我确实定义一个自定义持有者,是否可以从 py::class_
访问它?
英文:
I'm writing a pybind11 wrapper for a tuple-like class, and I'd like it have __iter__
method.
To do this, I convert the "tuple" fields to a vector of variants and define "__iter__"
like this:
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>()
);
Of course, this is a bad idea, because fields
will die as soon as it goes out of scope.
Other than defining a custom holder for MyTuple
(which would encapsulate this vector), is there a simple trick that could pass the ownership of this auxiliary vector to pybind11?
If I do define a custom holder, is it possible to access it from py::class_
?
答案1
得分: 1
如果你的元组类可以与 std::apply
一起使用(或者你可以执行类似的操作),你可以简单地创建一个 Python 元组并返回其迭代器:
.def("__iter__",
[](const MyTuple& t){
return py::iter(std::apply(
[](auto&&... args) {
return py::make_tuple(args...);
},
t));
}) // 这里不需要使用 keep_alive
你还可以创建一个自定义迭代器,例如:
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>()
)
第二个版本的优点是不会复制对象。
英文:
If your tuple class can be used with std::apply
(or if you can do something similar), you can simply create a Python tuple and return its iterator:
.def("__iter__",
[](const MyTuple& t){
return py::iter(std::apply(
[](auto&&... args) {
return py::make_tuple(args...);
},
t));
}) // no need for keep_alive here
You could also create a custom iterator, e.g.
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_;
};
Then you can simply:
.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>()
)
The second version has the advantage of not making copy of the objects.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论