Boost logo

Boost Users :

From: David Abrahams (dave_at_[hidden])
Date: 2006-01-08 11:51:35


Sergey Sikorskiy <sergey_sikorsky_at_[hidden]> writes:

> Hello everyone.
>
> I'm trying to map into python classes shown below.

Sergey,

The C++-sig (http://www.boost.org/more/mailing_lists.htm#cplussig) is
usually a better place to get your Boost.Python questions answered.

>
> class A
> {
> friend class B;
>
> public:
> ~A(void) {}
>
> private:
> A(B& b)
> : m_B(&b)
> {
> }
>
> B* m_B;
> };
>
> class B
> {
> friend class C;
>
> public:
> ~B(void) {}
>
> A* make_a(void)
> {
> return new A(*this);
> }
>
> private:
> B(C& c)
> : m_C(&c)
> {
> }
>
> C* m_C;
> };
>
> class C
> {
> public:
> ~C(void) {}
>
> B* make_b(void)
> {
> return new B(*this);
> }
> };
>
> C* make_c()
> {
> return new C();
> }
>
>
> Objects are supposed to be created in the order C, B, A and destroyed
> in the
> order A, B, C.
> I do not want to map constructors of A, B and C.
> I can see three ways to do what I want:
>
> 1)
> class_<A>("A", no_init);
> class_<B>("B", no_init)
> .def("make_a", &B::make_a, return_internal_reference<>())
> ;

Bad idea. That would only be appropriate if the resulting A object
were internal to (and its lifetime managed by) the B object. It is
not.

> class_<C>("C", no_init)
> .def("make_b", &C::make_b, return_internal_reference<>())
> ;
>
> def("make_c", make_c, return_value_policy<manage_new_object>());

Better. However, it doesn't account for the fact that the C object
must exist as long as the B object does. You need to compose your
return_value_policy with with_custodian_and_ward<0,1>.

>
> 2)

All three of these have the same problem.

> class_<A>("A", no_init);
> class_<B>("B", no_init)
> .def("make_a", &B::make_a,
> return_value_policy<manage_new_object>())
> ;

THe part below is the same as what you proposed in #1.

> class_<C>("C", no_init)
> .def("make_b", &C::make_b,
> return_value_policy<manage_new_object>())
> ;
>
> def("make_c", make_c, return_value_policy<manage_new_object>());
>
> 3)
> class_<A, shared_ptr<A>, noncopyable>("A", no_init);
> class_<B, shared_ptr<B>, noncopyable>("B", no_init)
> .def("make_a", &B::make_a,
> return_value_policy<manage_new_object>())
> ;
> class_<C, shared_ptr<C>, noncopyable>("C", no_init)
> .def("make_b", &C::make_b,
> return_value_policy<manage_new_object>())
> ;

I don't understand what you're trying to accomplish with noncopyable.

> def("make_c", make_c, return_value_policy<manage_new_object>());
>
> C::make_b and B::make_a are supposed to return shared_ptr<B> and
> shared_ptr<A> respectively in this case.

That gets you off the hook for return_value_policy<manage_new_object>,
but unless you're storing shared_ptrs in A, B, and C (rather than raw
pointers), you still need to use with_custodian_and_ward.
>
> After debugging this code I've got strange results:
> - In case 1 objects aren't get deleted at all;
> - In case 2 objects are get deleted in wrong order;
> - In case 3 objects are get deleted in wrong order;
>
> Cases 2 and three seem to be identical to me. The only difference I
> can see is that in case 3 I should manage objects lifetime by myself
> from C++ code. In opposite, in case 2 objects lifetime will be
> managed by python.
>
> I couldn't get an answer to my problem neither from documentation
> nor from Boost.Python tests nor from Google.
>
> Any explanations or suggestions will be greatly appreciated.

Have you read
http://www.boost.org/libs/python/doc/tutorial/doc/html/python/functions.html#python.call_policies?

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net