• 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