• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
6  #include <boost/python/converter/from_python.hpp>
7  #include <boost/python/converter/registrations.hpp>
8  #include <boost/python/converter/rvalue_from_python_data.hpp>
9  
10  #include <boost/python/object/find_instance.hpp>
11  
12  #include <boost/python/handle.hpp>
13  #include <boost/python/detail/raw_pyobject.hpp>
14  #include <boost/python/cast.hpp>
15  
16  #include <vector>
17  #include <algorithm>
18  
19  namespace boost { namespace python { namespace converter {
20  
21  // rvalue_from_python_stage1 -- do the first stage of a conversion
22  // from a Python object to a C++ rvalue.
23  //
24  //    source     - the Python object to be converted
25  //    converters - the registry entry for the target type T
26  //
27  // Postcondition: where x is the result, one of:
28  //
29  //   1. x.convertible == 0, indicating failure
30  //
31  //   2. x.construct == 0, x.convertible is the address of an object of
32  //      type T. Indicates a successful lvalue conversion
33  //
34  //   3. where y is of type rvalue_from_python_data<T>,
35  //      x.construct(source, y) constructs an object of type T
36  //      in y.storage.bytes and then sets y.convertible == y.storage.bytes,
37  //      or else throws an exception and has no effect.
rvalue_from_python_stage1(PyObject * source,registration const & converters)38  BOOST_PYTHON_DECL rvalue_from_python_stage1_data rvalue_from_python_stage1(
39      PyObject* source
40      , registration const& converters)
41  {
42      rvalue_from_python_stage1_data data;
43  
44      // First check to see if it's embedded in an extension class
45      // instance, as a special case.
46      data.convertible = objects::find_instance_impl(source, converters.target_type, converters.is_shared_ptr);
47          data.construct = 0;
48      if (!data.convertible)
49      {
50          for (rvalue_from_python_chain const* chain = converters.rvalue_chain;
51               chain != 0;
52               chain = chain->next)
53          {
54              void* r = chain->convertible(source);
55              if (r != 0)
56              {
57                  data.convertible = r;
58                  data.construct = chain->construct;
59                  break;
60              }
61          }
62      }
63      return data;
64  }
65  
66  // rvalue_result_from_python -- return the address of a C++ object which
67  // can be used as the result of calling a Python function.
68  //
69  //      src  - the Python object to be converted
70  //
71  //      data - a reference to the base part of a
72  //             rvalue_from_python_data<T> object, where T is the
73  //             target type of the conversion.
74  //
75  // Requires: data.convertible == &registered<T>::converters
76  //
rvalue_result_from_python(PyObject * src,rvalue_from_python_stage1_data & data)77  BOOST_PYTHON_DECL void* rvalue_result_from_python(
78      PyObject* src, rvalue_from_python_stage1_data& data)
79  {
80      // Retrieve the registration
81      // Cast in two steps for less-capable compilers
82      void const* converters_ = data.convertible;
83      registration const& converters = *static_cast<registration const*>(converters_);
84  
85      // Look for an eligible converter
86      data = rvalue_from_python_stage1(src, converters);
87      return rvalue_from_python_stage2(src, data, converters);
88  }
89  
rvalue_from_python_stage2(PyObject * source,rvalue_from_python_stage1_data & data,registration const & converters)90  BOOST_PYTHON_DECL void* rvalue_from_python_stage2(
91      PyObject* source, rvalue_from_python_stage1_data& data, registration const& converters)
92  {
93      if (!data.convertible)
94      {
95          handle<> msg(
96  #if PY_VERSION_HEX >= 0x03000000
97              ::PyUnicode_FromFormat
98  #else
99              ::PyString_FromFormat
100  #endif
101                  (
102                  "No registered converter was able to produce a C++ rvalue of type %s from this Python object of type %s"
103                  , converters.target_type.name()
104                  , source->ob_type->tp_name
105                  ));
106  
107          PyErr_SetObject(PyExc_TypeError, msg.get());
108          throw_error_already_set();
109      }
110  
111      // If a construct function was registered (i.e. we found an
112      // rvalue conversion), call it now.
113      if (data.construct != 0)
114          data.construct(source, &data);
115  
116      // Return the address of the resulting C++ object
117      return data.convertible;
118  }
119  
get_lvalue_from_python(PyObject * source,registration const & converters)120  BOOST_PYTHON_DECL void* get_lvalue_from_python(
121      PyObject* source
122      , registration const& converters)
123  {
124      // Check to see if it's embedded in a class instance
125      void* x = objects::find_instance_impl(source, converters.target_type);
126      if (x)
127          return x;
128  
129      lvalue_from_python_chain const* chain = converters.lvalue_chain;
130      for (;chain != 0; chain = chain->next)
131      {
132          void* r = chain->convert(source);
133          if (r != 0)
134              return r;
135      }
136      return 0;
137  }
138  
139  namespace
140  {
141    // Prevent looping in implicit conversions. This could/should be
142    // much more efficient, but will work for now.
143    typedef std::vector<rvalue_from_python_chain const*> visited_t;
144    static visited_t visited;
145  
visit(rvalue_from_python_chain const * chain)146    inline bool visit(rvalue_from_python_chain const* chain)
147    {
148        visited_t::iterator const p = std::lower_bound(visited.begin(), visited.end(), chain);
149        if (p != visited.end() && *p == chain)
150            return false;
151        visited.insert(p, chain);
152        return true;
153    }
154  
155    // RAII class for managing global visited marks.
156    struct unvisit
157    {
unvisitboost::python::converter::__anon96dfd1270111::unvisit158        unvisit(rvalue_from_python_chain const* chain)
159            : chain(chain) {}
160  
~unvisitboost::python::converter::__anon96dfd1270111::unvisit161        ~unvisit()
162        {
163            visited_t::iterator const p = std::lower_bound(visited.begin(), visited.end(), chain);
164            assert(p != visited.end());
165            visited.erase(p);
166        }
167     private:
168        rvalue_from_python_chain const* chain;
169    };
170  }
171  
172  
implicit_rvalue_convertible_from_python(PyObject * source,registration const & converters)173  BOOST_PYTHON_DECL bool implicit_rvalue_convertible_from_python(
174      PyObject* source
175      , registration const& converters)
176  {
177      if (objects::find_instance_impl(source, converters.target_type))
178          return true;
179  
180      rvalue_from_python_chain const* chain = converters.rvalue_chain;
181  
182      if (!visit(chain))
183          return false;
184  
185      unvisit protect(chain);
186  
187      for (;chain != 0; chain = chain->next)
188      {
189          if (chain->convertible(source))
190              return true;
191      }
192  
193      return false;
194  }
195  
196  namespace
197  {
throw_no_lvalue_from_python(PyObject * source,registration const & converters,char const * ref_type)198    void throw_no_lvalue_from_python(PyObject* source, registration const& converters, char const* ref_type)
199    {
200        handle<> msg(
201  #if PY_VERSION_HEX >= 0x03000000
202            ::PyUnicode_FromFormat
203  #else
204            ::PyString_FromFormat
205  #endif
206                (
207                "No registered converter was able to extract a C++ %s to type %s"
208                " from this Python object of type %s"
209                , ref_type
210                , converters.target_type.name()
211                , source->ob_type->tp_name
212                ));
213  
214        PyErr_SetObject(PyExc_TypeError, msg.get());
215  
216        throw_error_already_set();
217    }
218  
lvalue_result_from_python(PyObject * source,registration const & converters,char const * ref_type)219    void* lvalue_result_from_python(
220        PyObject* source
221        , registration const& converters
222        , char const* ref_type)
223    {
224        handle<> holder(source);
225        if (source->ob_refcnt <= 1)
226        {
227            handle<> msg(
228  #if PY_VERSION_HEX >= 0x3000000
229                ::PyUnicode_FromFormat
230  #else
231                ::PyString_FromFormat
232  #endif
233                    (
234                    "Attempt to return dangling %s to object of type: %s"
235                    , ref_type
236                    , converters.target_type.name()));
237  
238            PyErr_SetObject(PyExc_ReferenceError, msg.get());
239  
240            throw_error_already_set();
241        }
242  
243        void* result = get_lvalue_from_python(source, converters);
244        if (!result)
245            (throw_no_lvalue_from_python)(source, converters, ref_type);
246        return result;
247    }
248  
249  }
250  
throw_no_pointer_from_python(PyObject * source,registration const & converters)251  BOOST_PYTHON_DECL void throw_no_pointer_from_python(PyObject* source, registration const& converters)
252  {
253      (throw_no_lvalue_from_python)(source, converters, "pointer");
254  }
255  
throw_no_reference_from_python(PyObject * source,registration const & converters)256  BOOST_PYTHON_DECL void throw_no_reference_from_python(PyObject* source, registration const& converters)
257  {
258      (throw_no_lvalue_from_python)(source, converters, "reference");
259  }
260  
reference_result_from_python(PyObject * source,registration const & converters)261  BOOST_PYTHON_DECL void* reference_result_from_python(
262      PyObject* source
263      , registration const& converters)
264  {
265      return (lvalue_result_from_python)(source, converters, "reference");
266  }
267  
pointer_result_from_python(PyObject * source,registration const & converters)268  BOOST_PYTHON_DECL void* pointer_result_from_python(
269      PyObject* source
270      , registration const& converters)
271  {
272      if (source == Py_None)
273      {
274          Py_DECREF(source);
275          return 0;
276      }
277      return (lvalue_result_from_python)(source, converters, "pointer");
278  }
279  
void_result_from_python(PyObject * o)280  BOOST_PYTHON_DECL void void_result_from_python(PyObject* o)
281  {
282      Py_DECREF(expect_non_null(o));
283  }
284  
285  } // namespace boost::python::converter
286  
287  BOOST_PYTHON_DECL PyObject*
pytype_check(PyTypeObject * type_,PyObject * source)288  pytype_check(PyTypeObject* type_, PyObject* source)
289  {
290      if (!PyObject_IsInstance(source, python::upcast<PyObject>(type_)))
291      {
292          ::PyErr_Format(
293              PyExc_TypeError
294              , "Expecting an object of type %s; got an object of type %s instead"
295              , type_->tp_name
296              , source->ob_type->tp_name
297              );
298          throw_error_already_set();
299      }
300      return source;
301  }
302  
303  }} // namespace boost::python
304