提升 python 容器、迭代器和项的生存期
Boost python container, iterator and item lifetimes
我正在尝试向Python公开C++容器。 我有:
class Container {
std::auto_ptr<Iterator> __iter__();
};
class Iterator {
Container & parent;
Item __next__();
};
class Item {
Container & parent;
};
Item
类在内部引用容器中存在的数据。 返回Item
实例的Iterator
不必存在,Item
就可以使用。
c = Container()
for i in c:
store = i
print store
在上面的代码中,我希望得到Container
,Iterator
和很少的Item
实例。 当它到达print
语句时,我希望Iterator
已经被破坏,但Container
实例显然仍然存在store
.
现在问题来了。我不知道使用什么CallPolicy
来实现这种效果: 定义:
class_<Container>("Container", ...)
.def("__iter__", &Container::__iter__, return_interal_reference<>() )
;
class_<Iterator>("Iterator", ...)
.def("next", &Iterator::__next__, what_call_policy_here? )
;
class_<Item>("Item", ...)
.def("__str__", ... )
;
我应该用什么来代替what_call_policy_here
?
好的,所以经过长时间的挖掘,我想我想出了对暴露类型透明的解决方案。
简短描述
基本上解决方案是创建CallPolicy
,它将自动存储对父对象的引用(即Container
) 在返回的子对象内(即Iterator
)作为其属性(我使用了一个私人名称,但Python在这方面非常自由)。
然后自动将其复制到所有同级对象(同级是父级的另一个子级,但该子级是使用调用另一个子级的方法创建的,因此不是直接从父级创建的)。
实现
这需要对CallPolicy
进行一些摆弄。我必须创建两个自定义的:
// This policy is used for methods returning items that require object to be
// kept as long as return thing is alive.
// It stores reference in attribute named Property_::name
template <typename Property_, class BasePolicy_ = boost::python::default_call_policies>
struct store_parent_reference: public BasePolicy_
{
template <class ArgumentPackage>
static PyObject* postcall(ArgumentPackage const& args_, PyObject* result)
{
result = BasePolicy_::postcall(args_, result);
PyObject* parent = detail::get_prev< std::size_t(1) >::execute(args_, result);
PyObject* child = result;
if( PyObject_SetAttrString( child, Property_::name, parent ) == -1 )
{
std::ostringstream err;
err << "store_parent_reference::postcall could not set attribute `" << Property_::name
<< "` on newly allocated object `"
<< extract<std::string>( object( handle<>(borrowed(child))).attr("__str__")() )()
<< "`";
throw std::runtime_error(err.str());
}
return result;
}
};
// This policy is used for methods returning "sibling" in the meaning both the returned object
// and one that has this method called on require "parent" object to be alive.
//
// It copies reference to "parent" to attribute named ChildProperty_::name
// from "original" object's attribute named SiblingProperty_::name
template <typename ChildProperty_, typename SiblingProperty_ = ChildProperty_, class BasePolicy_ = boost::python::default_call_policies>
struct copy_parent_from_sibling: public BasePolicy_
{
template <class ArgumentPackage>
static PyObject* postcall(ArgumentPackage const& args_, PyObject* result)
{
result = BasePolicy_::postcall(args_, result);
PyObject* sibling = detail::get_prev< std::size_t(1) >::execute(args_, result);
PyObject* new_child = result;
PyObject* parent = PyObject_GetAttrString( sibling, SiblingProperty_::name );
if( parent == NULL )
{
std::ostringstream err;
err << "copy_parent_from_sibling::postcall could not get attribute `"
<< SiblingProperty_::name
<< "` from sibling `"
<< extract<std::string>( object( handle<>(borrowed(sibling))).attr("__str__")() )()
<< "` to set up attribute `"
<< ChildProperty_::name
<< "` of returned object which is `"
<< extract<std::string>( object( handle<>(borrowed(new_child))).attr("__str__")() )()
<< "`";
throw std::runtime_error(err.str());
}
if( PyObject_SetAttrString( new_child, ChildProperty_::name, parent ) == -1 )
{
std::ostringstream err;
err << "copy_parent_from_sibling::postcall could not set attribute `"
<< ChildProperty_::name
<< "` on returned object which is `"
<< extract<std::string>( object( handle<>(borrowed(new_child))).attr("__str__")() )()
<< "`";
throw std::runtime_error(err.str());
}
Py_DECREF(parent);
return result;
}
};
用法
现在用法:
struct ContainerProperty {
static const char * const name;
};
const char * const ContainerProperty::name = "__container"
class_<Container>("Container", ...)
.def("__iter__", &Container::__iter__, store_parent_reference< ContainerProperty >() )
;
class_<Iterator>("Iterator", ...)
.def("next", &Iterator::__next__, copy_parent_from_sibling< ContainerProperty >() )
;
class_<Item>("Item", ...)
;
注意:很难为boost::python
的东西提供完整的最小工作样本,所以我可能错过了上面的一些细节,但解决方案对我来说似乎工作正常(我正在跟踪析构函数调用进行检查)。
此外,这不是唯一的解决方案。请注意,store_parent_reference
与return_internal_reference
有些相似,不同之处在于它明确需要一个存储数据的位置。这只是因为copy_parent_from_sibling
需要从某个地方复制它。
这种方法的主要好处是它不需要原始类来了解Python的东西。
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++中带有List类的迭代器Segfault
- 如何在c++迭代器类型中包装std::chrono
- 集合上的输出迭代器:assign和increment迭代器
- Boost Spirit,获取迭代器内部语义动作
- 对于set上的循环-获取next元素迭代器
- 为什么output_editor Concept不需要output_e迭代器标记
- c++17文件系统::recursive_directory迭代器()在mac上没有给出这样的目录,但在windows上
- 使用迭代器时如何访问对象在向量中的位置?
- std::vector::迭代器是否可以合法地作为指针
- 跟随整数索引列表的自定义类迭代器
- 不明白迭代器,引用和指针失效,一个例子
- 我可以使用反向迭代器作为ForwardIt吗
- ESP8266单片机矢量迭代器的C++问题
- 如何在C++中将迭代器作为函数参数传递
- 是否应避免从非常量迭代器转换为常量迭代器?
- 如何在 c++ 中将字符串迭代器变量传递给函数?
- 为什么 vector 的随机访问迭代器给出与指针不同的内存地址?
- 提升 python 容器、迭代器和项的生存期