boostorg/python — pull request #8 by ukoethe (2014-05-11 11:24:51+00:00)
Title: added 'construct_custodian_for' and test

(note: I will update the documentation as well when this pull request gets accepted)

This pull request proposes a fixe for the long-standing problem that `with_custodian_and_ward_postcall` doesn't work in connection with `make_constructor` (see e.g. https://mail.python.org/pipermail/cplusplus-sig/2007-June/012145.html).

Consider the following class definitions:
```c++
struct A {
int i_;

A(int i)
: i_(i)
{}
};

struct B {
A * a_;

B(A * a)
: a_(a)
{}

int a_content()
{
return a == 0 ? 0 : a->i_;
}
};

B * create_B(A * a)
{
return new B(a);
}
```
and the corresponding boost::python glue code
```c++
BOOST_PYTHON_MODULE(test_custodian_and_ward)
{
class_("A", no_init)
.def(init())
// #1
.def("create_B", &create_B, with_custodian_and_ward_postcall<0,1,
return_value_policy >())
;

class_("B1", no_init)
// #2
.def(init()[with_custodian_and_ward_postcall<1,2>()])
.def("a_content", &B::a_content)
;

class_("B2", no_init)
// #3
.def("__init__", make_constructor(&create_B, with_custodian_and_ward_postcall<1,2>()))
.def("a_content", &B::a_content)
;

class_("B3", no_init)
// #4
.def("__init__", make_constructor(&create_B, with_custodian_and_ward_postcall<0,1>()))
.def("a_content", &B::a_content)
;
}
```
In this code
* `#1` works as desired
* `#2` works, but is a bit counter-intuitive because indexing suddenly starts at 1
* `#3` causes an "argument index out of range" error
* `#4` runs, but fails to establish the desired custodian-and-ward relationship (i.e. `B3.a_content()` may crash because the containd `A` instance can be deleted prematurely)

The pull request introduces a new policy `construct_custodian_for` which solves problems `#2-4`. It is used like this:
```c++
BOOST_PYTHON_MODULE(test_custodian_and_ward)
{
...

class_("B1", no_init)
.def(init()[construct_custodian_for<1>()])
.def("a_content", &B::a_content)
;

class_("B2", no_init)
.def("__init__", make_constructor(&create_B, construct_custodian_for<1>()))
.def("a_content", &B::a_content)
;
}
```