Boost python容器,迭代器和项目生命周期 [英] Boost python container, iterator and item lifetimes

查看:220
本文介绍了Boost python容器,迭代器和项目生命周期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图将C ++容器暴露给Python。
我有:

  class Container {
std :: auto_ptr< Iterator& __iter __();
};

class Iterator {
Container&父母
Item __next __();
};

class Item {
Container&父母
};

容器。 其中返回项实例不必存在 c>可用。

  c = Container()
for i in c:
store = i

print store

在上面的代码中,我期望得到 Container Iterator 和少数实例。
当它到达 print 语句时,我期望 Iterator 已经被销毁,但

$



现在这个问题。我不知道 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 )的引用(即 / code>)作为其属性(我使用了一个私有名称,但是Python在这方面相当自由)。



> sibling 对象( sibling parent 的另一个,而是通过调用另一个



实施



这需要用 CallPolicy 来处理。我必须创建两个自定义的:

  //此策略用于返回需要对象为
//保持只要return的东西是活着的。
//在名为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 can not set attribute`<<属性_ :: name
<< `on newly allocated object
<<提取< std :: string>(object(handle<>(borrowed(child)))。attr(__ str __)())()
& `;
throw std :: runtime_error(err.str());
}



返回结果;
}
};


//此策略用于返回sibling的方法,返回的对象
//和具有此方法的requireparent对象活着。
//
//将对parent的引用复制到名为ChildProperty的属性_ :: name
//从original对象的属性SiblingProperty _ :: name中复制
模板< 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`
<<提取< std :: string>(object(handle<>(borrowed(sibling)))。attr(__ str __)())()
& `设置属性`
<< ChildProperty _ :: name
<< `的返回对象是`
<<提取< 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 can not set attribute`
<< ChildProperty _ :: name
<< `on返回的对象是`
<<提取< std :: string>(object(handle<>(borrowed(new_child)))。attr(__ str __)())()
& `;
throw std :: runtime_error(err.str());
}

Py_DECREF(parent);

返回结果;
}
};



用法



/ p>

  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的东西。


I am trying to expose C++ container to Python. I have:

class Container {
    std::auto_ptr<Iterator> __iter__();
};

class Iterator {
    Container & parent;
    Item __next__();
};

class Item {
    Container & parent;
};

Item class internally references data that exists in Container. Iterator which returned Item instance doesn't have to exist for Item to be usable.

c = Container()
for i in c:
    store = i

print store

In above code I would expect to get Container, Iterator and few Item instances. When it reaches print statement I would expect Iterator to be already destructed, but Container instance has obviously still exist for store.

Now here comes the problem. I don't know what CallPolicy to use to achieve that effect: Defining:

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 should I use in place of what_call_policy_here?

解决方案

Ok, so after long digging I think I came up with solution that is transparent to exposed type.

Short description

Basically solution was to create CallPolicy that will automatically store reference to parent object (i.e. Container) inside returned child object (i.e. Iterator) as its attribute (I used a private name, but Python is quite liberal in that matter).

Then automatically copy this to all sibling objects (sibling is another child of parent, but one that was created with a call to method of another child, so not directly from parent).

Implementation

This required a bit of fiddling with CallPolicy. I had to create two custom ones:

// 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;
    }
};

Usage

Now the usage:

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", ...)
;

Note: It is hard to give complete minimal working sample for boost::python stuff, so I might have missed some detail above, but solution seems to be working fine for me (I was tracking destructor calls to check).

Also this is not the only solution. Notice that store_parent_reference is somewhat similar to return_internal_reference with the difference it explicitly needs a place to store data. This is only because copy_parent_from_sibling needs to copy it from somewhere.

Main benefit of this approach is that it does not require original classes to be aware of Python stuff.

这篇关于Boost python容器,迭代器和项目生命周期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆