Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/python/src/converter/from_python.cpp @ 29

Last change on this file since 29 was 29, checked in by landauf, 17 years ago

updated boost from 1_33_1 to 1_34_1

File size: 8.4 KB
Line 
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
19namespace 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.
38BOOST_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    if (data.convertible)
48    {
49        data.construct = 0;
50    }
51    else
52    {
53        for (rvalue_from_python_chain const* chain = converters.rvalue_chain;
54             chain != 0;
55             chain = chain->next)
56        {
57            void* r = chain->convertible(source);
58            if (r != 0)
59            {
60                data.convertible = r;
61                data.construct = chain->construct;
62                break;
63            }
64        }
65    }
66    return data;
67}
68
69// rvalue_result_from_python -- return the address of a C++ object which
70// can be used as the result of calling a Python function.
71//
72//      src  - the Python object to be converted
73//
74//      data - a reference to the base part of a
75//             rvalue_from_python_data<T> object, where T is the
76//             target type of the conversion.
77//
78// Requires: data.convertible == &registered<T>::converters
79//
80BOOST_PYTHON_DECL void* rvalue_result_from_python(
81    PyObject* src, rvalue_from_python_stage1_data& data)
82{
83    // Retrieve the registration
84    // Cast in two steps for less-capable compilers
85    void const* converters_ = data.convertible;
86    registration const& converters = *static_cast<registration const*>(converters_);
87
88    // Look for an eligible converter
89    data = rvalue_from_python_stage1(src, converters);
90    return rvalue_from_python_stage2(src, data, converters);
91}
92
93BOOST_PYTHON_DECL void* rvalue_from_python_stage2(
94    PyObject* source, rvalue_from_python_stage1_data& data, registration const& converters)
95{
96    if (!data.convertible)
97    {
98        handle<> msg(
99            ::PyString_FromFormat(
100                "No registered converter was able to produce a C++ rvalue of type %s from this Python object of type %s"
101                , converters.target_type.name()
102                , source->ob_type->tp_name
103                ));
104             
105        PyErr_SetObject(PyExc_TypeError, msg.get());
106        throw_error_already_set();
107    }
108
109    // If a construct function was registered (i.e. we found an
110    // rvalue conversion), call it now.
111    if (data.construct != 0)
112        data.construct(source, &data);
113
114    // Return the address of the resulting C++ object
115    return data.convertible;
116}
117
118BOOST_PYTHON_DECL void* get_lvalue_from_python(
119    PyObject* source
120    , registration const& converters)
121{
122    // Check to see if it's embedded in a class instance
123    void* x = objects::find_instance_impl(source, converters.target_type);
124    if (x)
125        return x;
126
127    lvalue_from_python_chain const* chain = converters.lvalue_chain;
128    for (;chain != 0; chain = chain->next)
129    {
130        void* r = chain->convert(source);
131        if (r != 0)
132            return r;
133    }
134    return 0;
135}
136
137namespace
138{
139  // Prevent looping in implicit conversions. This could/should be
140  // much more efficient, but will work for now.
141  typedef std::vector<rvalue_from_python_chain const*> visited_t;
142  static visited_t visited;
143
144  inline bool visit(rvalue_from_python_chain const* chain)
145  {
146      visited_t::iterator const p = std::lower_bound(visited.begin(), visited.end(), chain);
147      if (p != visited.end() && *p == chain)
148          return false;
149      visited.insert(p, chain);
150      return true;
151  }
152
153  // RAII class for managing global visited marks.
154  struct unvisit
155  {
156      unvisit(rvalue_from_python_chain const* chain)
157          : chain(chain) {}
158     
159      ~unvisit()
160      {
161          visited_t::iterator const p = std::lower_bound(visited.begin(), visited.end(), chain);
162          assert(p != visited.end());
163          visited.erase(p);
164      }
165   private:
166      rvalue_from_python_chain const* chain;
167  };
168}
169
170
171BOOST_PYTHON_DECL bool implicit_rvalue_convertible_from_python(
172    PyObject* source
173    , registration const& converters)
174{   
175    if (objects::find_instance_impl(source, converters.target_type))
176        return true;
177   
178    rvalue_from_python_chain const* chain = converters.rvalue_chain;
179   
180    if (!visit(chain))
181        return false;
182
183    unvisit protect(chain);
184   
185    for (;chain != 0; chain = chain->next)
186    {
187        if (chain->convertible(source))
188            return true;
189    }
190
191    return false;
192}
193
194namespace
195{
196  void throw_no_lvalue_from_python(PyObject* source, registration const& converters, char const* ref_type)
197  {
198      handle<> msg(
199          ::PyString_FromFormat(
200              "No registered converter was able to extract a C++ %s to type %s"
201              " from this Python object of type %s"
202              , ref_type
203              , converters.target_type.name()
204              , source->ob_type->tp_name
205              ));
206             
207      PyErr_SetObject(PyExc_TypeError, msg.get());
208
209      throw_error_already_set();
210  }
211
212  void* lvalue_result_from_python(
213      PyObject* source
214      , registration const& converters
215      , char const* ref_type)
216  {
217      handle<> holder(source);
218      if (source->ob_refcnt <= 1)
219      {
220          handle<> msg(
221              ::PyString_FromFormat(
222                  "Attempt to return dangling %s to object of type: %s"
223                  , ref_type
224                  , converters.target_type.name()));
225         
226          PyErr_SetObject(PyExc_ReferenceError, msg.get());
227
228          throw_error_already_set();
229      }
230     
231      void* result = get_lvalue_from_python(source, converters);
232      if (!result)
233          (throw_no_lvalue_from_python)(source, converters, ref_type);
234      return result;
235  }
236 
237}
238
239BOOST_PYTHON_DECL void throw_no_pointer_from_python(PyObject* source, registration const& converters)
240{
241    (throw_no_lvalue_from_python)(source, converters, "pointer");
242}
243
244BOOST_PYTHON_DECL void throw_no_reference_from_python(PyObject* source, registration const& converters)
245{
246    (throw_no_lvalue_from_python)(source, converters, "reference");
247}
248
249BOOST_PYTHON_DECL void* reference_result_from_python(
250    PyObject* source
251    , registration const& converters)
252{
253    return (lvalue_result_from_python)(source, converters, "reference");
254}
255 
256BOOST_PYTHON_DECL void* pointer_result_from_python(
257    PyObject* source
258    , registration const& converters)
259{
260    if (source == Py_None)
261    {
262        Py_DECREF(source);
263        return 0;
264    }
265    return (lvalue_result_from_python)(source, converters, "pointer");
266}
267 
268BOOST_PYTHON_DECL void void_result_from_python(PyObject* o)
269{
270    Py_DECREF(expect_non_null(o));
271}
272
273} // namespace boost::python::converter
274
275BOOST_PYTHON_DECL PyObject*
276pytype_check(PyTypeObject* type_, PyObject* source)
277{
278    if (!PyObject_IsInstance(source, python::upcast<PyObject>(type_)))
279    {
280        ::PyErr_Format(
281            PyExc_TypeError
282            , "Expecting an object of type %s; got an object of type %s instead"
283            , type_->tp_name
284            , source->ob_type->tp_name
285            );
286        throw_error_already_set();
287    }
288    return source;
289}
290
291}} // namespace boost::python
Note: See TracBrowser for help on using the repository browser.