2012-11-27 26 views
1

我想向C++公开C++容器。 我:Boost python容器,迭代器和项目生存期

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

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

class Item { 
    Container & parent; 
}; 

Item类内部引用存在于集装箱数据。 Iterator其中返回Item实例不必为Item存在可用。

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

print store 

在上面的代码中,我期望得到ContainerIterator很少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使用?

回答

2

好吧,经过长时间的挖掘,我想我想出了对暴露类型透明的解决方案。

简短说明

基本上解决方案是创建CallPolicy,将自动存储参照对象(即Container)内返回对象(即Iterator)作为其属性(I使用的专用名称,但是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_referencereturn_internal_reference有点类似,但它明显需要一个地方来存储数据。这只是因为copy_parent_from_sibling需要从某处复制它。

这种方法的主要好处是它不需要原始类知道Python的东西。