source: trunk/src/pyconversions.h

Last change on this file was 575, checked in by mar637, 19 years ago

STL <-> python sequence conversions

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 7.8 KB
Line 
1/* -*- mode:c++ -*- */
2
3/** @file
4 
5Boost.Python conversion functions from STL containers to Python
6sequences and vice versa.
7
8$Id: pyconversions.h 575 2005-04-07 02:11:59Z mar637 $
9
10*/
11
12#ifndef PY_CONVERSIONS_H
13#define PY_CONVERSIONS_H
14
15#include <vector>
16
17#include <boost/python.hpp>
18
19using namespace boost::python;
20
21/** A wrapper of a conversion function to convert a STL vector to a
22    Python tuple.  This class satisfies the requirements of the
23    boost::python::to_python_converter conversion template argument.
24
25    Copied from
26    scitbx/include/scitbx/boost_python/container_conversions.h that is
27    described in the <a
28    href="http://www.boost.org/libs/python/doc/v2/faq.html">
29    Boost.Python FAQ. </a>
30
31    @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
32    <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
33*/
34  template < typename ContainerType >
35  struct to_tuple
36  {
37    /** Creates and returns a Python @c tuple from the elements copied
38        from a STL container. The @c ContainerType must be a vector,
39        but may contain any type of object supported by the
40        boost::python::object constructor. */
41    static PyObject* convert (ContainerType const& c)
42    {
43      using boost::python::incref; // works around gcc 2.96 bug
44      using boost::python::list; // dito
45      list result;
46      typename ContainerType::const_iterator i = c.begin();
47      for( ; i != c.end(); ++i)
48        {
49          result.append(*i);
50        }
51      return incref(tuple(result).ptr());
52    }
53  };
54
55/** Converts an STL vector of T objects to Python tuple.
56
57    Copied from
58    scitbx/include/scitbx/boost_python/container_conversions.h that is
59    described in the <a
60    href="http://www.boost.org/libs/python/doc/v2/faq.html">
61    Boost.Python FAQ. </a>
62
63    @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
64    <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
65*/
66  template < typename T >
67  struct std_vector_to_tuple
68  {
69    std_vector_to_tuple ()
70    {
71      to_python_converter < std::vector < T >,
72                            to_tuple < std::vector < T > >  > ();
73    }
74  };
75
76// /** Interface to AxesType enumeration. */
77// struct AxesOwner
78// {
79//   public:
80//     AxesOwner(){};
81//     typedef AxesType enum_type;
82// };
83
84// /** Conversion of AxesType enumeration. */
85// struct EnumTypeConverters
86//   : python::enum_as_int_converters <AxesOwner::enum_type>
87// {
88// };
89
90/** Default operations on all containers for conversion from Python
91    container to C++ one.
92
93    Copied from
94    scitbx/include/scitbx/boost_python/container_conversions.h that is
95    described in the <a
96    href="http://www.boost.org/libs/python/doc/v2/faq.html">
97    Boost.Python FAQ. </a>
98
99    @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
100    <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
101*/
102  struct default_policy
103  {
104    static bool check_convertibility_per_element() { return false; }
105
106    template <typename ContainerType>
107    static bool check_size(boost::type<ContainerType>, std::size_t sz)
108    {
109      return true;
110    }
111
112    template <typename ContainerType>
113    static void assert_size(boost::type<ContainerType>, std::size_t sz) {}
114
115    template <typename ContainerType>
116    static void reserve(ContainerType& a, std::size_t sz) {}
117  };
118
119/** Operations on containers that have variable capacity for
120    conversion from Python container to C++ one.
121
122    Copied from
123    scitbx/include/scitbx/boost_python/container_conversions.h that is
124    described in the <a
125    href="http://www.boost.org/libs/python/doc/v2/faq.html">
126    Boost.Python FAQ. </a>
127
128    @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
129    <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
130*/
131  struct variable_capacity_policy : default_policy
132  {
133    template <typename ContainerType>
134    static void reserve(ContainerType& a, std::size_t sz)
135    {
136      a.reserve(sz);
137    }
138
139    template <typename ContainerType, typename ValueType>
140    static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
141    {
142      assert(a.size() == i);
143      a.push_back(v);
144    }
145  };
146
147/** Conversion of Python sequence to C++ container.
148
149    Copied from
150    scitbx/include/scitbx/boost_python/container_conversions.h that is
151    described in the <a
152    href="http://www.boost.org/libs/python/doc/v2/faq.html">
153    Boost.Python FAQ. </a>
154
155    @author Ralf W. Grosse-Kunstleve <rwgk@yahoo.com> of
156    <a href="http://www.lbl.gov/">Lawrence Berkeley National Laboratory</a>
157*/
158  template <typename ContainerType, typename ConversionPolicy>
159  struct from_python_sequence
160  {
161    typedef typename ContainerType::value_type container_element_type;
162
163    from_python_sequence()
164    {
165      boost::python::converter::registry::push_back(
166        &convertible,
167        &construct,
168        boost::python::type_id<ContainerType>());
169    }
170
171    /** Appears to return @a obj_ptr if it is type of Python sequence
172        that can be convertible to C++ container. */
173    static void* convertible(PyObject* obj_ptr)
174    {
175      using namespace boost::python;
176      using boost::python::allow_null; // works around gcc 2.96 bug
177      {
178        // Restriction to list, tuple, iter, xrange until
179        // Boost.Python overload resolution is enhanced.
180         //
181         // add PySequence_Check() for numarray.
182         //
183         if (!(   PyList_Check(obj_ptr)
184                  || PyTuple_Check(obj_ptr)
185                  || PyIter_Check(obj_ptr)
186                  || PyRange_Check(obj_ptr)
187                  || PySequence_Check(obj_ptr) )) return 0;
188      }
189      handle<> obj_iter(allow_null(PyObject_GetIter(obj_ptr)));
190      if (!obj_iter.get()) { // must be convertible to an iterator
191        PyErr_Clear();
192        return 0;
193      }
194      if (ConversionPolicy::check_convertibility_per_element()) {
195        int obj_size = PyObject_Length(obj_ptr);
196        if (obj_size < 0) { // must be a measurable sequence
197          PyErr_Clear();
198          return 0;
199        }
200        if (!ConversionPolicy::check_size(
201          boost::type<ContainerType>(), obj_size)) return 0;
202        bool is_range = PyRange_Check(obj_ptr);
203        //std::size_t i=0;
204                int i = 0;
205#ifndef _MSC_VER // because it causes c1001: internal compiler error
206        for(;;i++) {
207          handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get())));
208          if (PyErr_Occurred()) {
209            PyErr_Clear();
210            return 0;
211          }
212          if (!py_elem_hdl.get()) break; // end of iteration
213          object py_elem_obj(py_elem_hdl);
214          extract<container_element_type> elem_proxy(py_elem_obj);
215          if (!elem_proxy.check()) return 0;
216          if (is_range) break; // in a range all elements are of the same type
217        }
218        if (!is_range) assert(i == obj_size );
219#endif
220      }
221      return obj_ptr;
222    }
223
224    /** Constructs a C++ container from a Python sequence. */
225    static void construct(
226      PyObject* obj_ptr,
227      boost::python::converter::rvalue_from_python_stage1_data* data)
228    {
229      using namespace boost::python;
230      using boost::python::allow_null; // works around gcc 2.96 bug
231      using boost::python::converter::rvalue_from_python_storage; // dito
232      using boost::python::throw_error_already_set; // dito
233      handle<> obj_iter(PyObject_GetIter(obj_ptr));
234      void* storage = (
235        (rvalue_from_python_storage<ContainerType>*)
236          data)->storage.bytes;
237      new (storage) ContainerType();
238      data->convertible = storage;
239      ContainerType& result = *((ContainerType*)storage);
240      std::size_t i=0;
241      for(;;i++) {
242        handle<> py_elem_hdl(allow_null(PyIter_Next(obj_iter.get())));
243        if (PyErr_Occurred()) throw_error_already_set();
244        if (!py_elem_hdl.get()) break; // end of iteration
245        object py_elem_obj(py_elem_hdl);
246        extract<container_element_type> elem_proxy(py_elem_obj);
247        ConversionPolicy::set_value(result, i, elem_proxy());
248      }
249      ConversionPolicy::assert_size(boost::type<ContainerType>(), i);
250    }
251
252  };
253
254#endif // PY_CONVERSIONS_H
Note: See TracBrowser for help on using the repository browser.