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
CallPolicythat 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:Usage
Now the usage:
Note: It is hard to give complete minimal working sample for
boost::pythonstuff, 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_referenceis somewhat similar toreturn_internal_referencewith the difference it explicitly needs a place to store data. This is only becausecopy_parent_from_siblingneeds to copy it from somewhere.Main benefit of this approach is that it does not require original classes to be aware of Python stuff.