1 // Copyright David Abrahams 2002.
2 // Distributed under the Boost Software License, Version 1.0. (See
3 // accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 #ifndef WITH_CUSTODIAN_AND_WARD_DWA2002131_HPP
6 # define WITH_CUSTODIAN_AND_WARD_DWA2002131_HPP
7 
8 # include <boost/python/detail/prefix.hpp>
9 
10 # include <boost/python/default_call_policies.hpp>
11 # include <boost/python/object/life_support.hpp>
12 # include <algorithm>
13 
14 namespace boost { namespace python {
15 
16 namespace detail
17 {
18   template <std::size_t N>
19   struct get_prev
20   {
21       template <class ArgumentPackage>
executeboost::python::detail::get_prev22       static PyObject* execute(ArgumentPackage const& args, PyObject* = 0)
23       {
24           int const pre_n = static_cast<int>(N) - 1; // separate line is gcc-2.96 workaround
25           return detail::get(mpl::int_<pre_n>(), args);
26       }
27   };
28   template <>
29   struct get_prev<0>
30   {
31       template <class ArgumentPackage>
executeboost::python::detail::get_prev32       static PyObject* execute(ArgumentPackage const&, PyObject* zeroth)
33       {
34           return zeroth;
35       }
36   };
37 }
38 template <
39     std::size_t custodian
40   , std::size_t ward
41   , class BasePolicy_ = default_call_policies
42 >
43 struct with_custodian_and_ward : BasePolicy_
44 {
45     BOOST_STATIC_ASSERT(custodian != ward);
46     BOOST_STATIC_ASSERT(custodian > 0);
47     BOOST_STATIC_ASSERT(ward > 0);
48 
49     template <class ArgumentPackage>
precallboost::python::with_custodian_and_ward50     static bool precall(ArgumentPackage const& args_)
51     {
52         unsigned arity_ = detail::arity(args_);
53         if (custodian > arity_ || ward > arity_)
54         {
55             PyErr_SetString(
56                 PyExc_IndexError
57               , "boost::python::with_custodian_and_ward: argument index out of range"
58             );
59             return false;
60         }
61 
62         PyObject* patient = detail::get_prev<ward>::execute(args_);
63         PyObject* nurse = detail::get_prev<custodian>::execute(args_);
64 
65         PyObject* life_support = python::objects::make_nurse_and_patient(nurse, patient);
66         if (life_support == 0)
67             return false;
68 
69         bool result = BasePolicy_::precall(args_);
70 
71         if (!result) {
72             Py_DECREF(life_support);
73         }
74 
75         return result;
76     }
77 };
78 
79 template <std::size_t custodian, std::size_t ward, class BasePolicy_ = default_call_policies>
80 struct with_custodian_and_ward_postcall : BasePolicy_
81 {
82     BOOST_STATIC_ASSERT(custodian != ward);
83 
84     template <class ArgumentPackage>
postcallboost::python::with_custodian_and_ward_postcall85     static PyObject* postcall(ArgumentPackage const& args_, PyObject* result)
86     {
87         std::size_t arity_ = detail::arity(args_);
88         // check if either custodian or ward exceeds the arity
89         // (this weird formulation avoids "always false" warnings
90         // for arity_ = 0)
91         if ( (std::max)(custodian, ward) > arity_ )
92         {
93             PyErr_SetString(
94                 PyExc_IndexError
95               , "boost::python::with_custodian_and_ward_postcall: argument index out of range"
96             );
97             return 0;
98         }
99 
100         PyObject* patient = detail::get_prev<ward>::execute(args_, result);
101         PyObject* nurse = detail::get_prev<custodian>::execute(args_, result);
102 
103         if (nurse == 0) return 0;
104 
105         result = BasePolicy_::postcall(args_, result);
106         if (result == 0)
107             return 0;
108 
109         if (python::objects::make_nurse_and_patient(nurse, patient) == 0)
110         {
111             Py_XDECREF(result);
112             return 0;
113         }
114         return result;
115     }
116 };
117 
118 
119 }} // namespace boost::python
120 
121 #endif // WITH_CUSTODIAN_AND_WARD_DWA2002131_HPP
122