librcsb-core-wrapper  1.000
container_conversions.h
Go to the documentation of this file.
1 #ifndef SCITBX_BOOST_PYTHON_CONTAINER_CONVERSIONS_H
2 #define SCITBX_BOOST_PYTHON_CONTAINER_CONVERSIONS_H
3 
4 #include <boost/python/list.hpp>
5 #include <boost/python/tuple.hpp>
6 #include <boost/python/extract.hpp>
7 #include <boost/python/to_python_converter.hpp>
8 
9 namespace scitbx { namespace boost_python { namespace container_conversions {
10 
11  template <typename ContainerType>
12  struct to_tuple
13  {
14  static PyObject* convert(ContainerType const& a)
15  {
16  boost::python::list result;
17  typedef typename ContainerType::const_iterator const_iter;
18  for(const_iter p=a.begin();p!=a.end();p++) {
19  result.append(boost::python::object(*p));
20  }
21  return boost::python::incref(boost::python::tuple(result).ptr());
22  }
23 
24  static const PyTypeObject* get_pytype() { return &PyTuple_Type; }
25  };
26 
28  {
29  static bool check_convertibility_per_element() { return false; }
30 
31  template <typename ContainerType>
32  static bool check_size(boost::type<ContainerType>, std::size_t /*sz*/)
33  {
34  return true;
35  }
36 
37  template <typename ContainerType>
38  static void assert_size(boost::type<ContainerType>, std::size_t /*sz*/) {}
39 
40  template <typename ContainerType>
41  static void reserve(ContainerType& a, std::size_t sz) {}
42  };
43 
45  {
46  static bool check_convertibility_per_element() { return true; }
47 
48  template <typename ContainerType>
49  static bool check_size(boost::type<ContainerType>, std::size_t sz)
50  {
51  return ContainerType::size() == sz;
52  }
53 
54  template <typename ContainerType>
55  static void assert_size(boost::type<ContainerType>, std::size_t sz)
56  {
57  if (!check_size(boost::type<ContainerType>(), sz)) {
58  PyErr_SetString(PyExc_RuntimeError,
59  "Insufficient elements for fixed-size array.");
60  boost::python::throw_error_already_set();
61  }
62  }
63 
64  template <typename ContainerType>
65  static void reserve(ContainerType& /*a*/, std::size_t sz)
66  {
67  if (sz > ContainerType::size()) {
68  PyErr_SetString(PyExc_RuntimeError,
69  "Too many elements for fixed-size array.");
70  boost::python::throw_error_already_set();
71  }
72  }
73 
74  template <typename ContainerType, typename ValueType>
75  static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
76  {
77  reserve(a, i+1);
78  a[i] = v;
79  }
80  };
81 
83  {
84  template <typename ContainerType>
85  static void reserve(ContainerType& a, std::size_t sz)
86  {
87  a.reserve(sz);
88  }
89 
90  template <typename ContainerType, typename ValueType>
91  static void set_value(
92  ContainerType& a,
93  std::size_t
94 #if !defined(NDEBUG)
95  i
96 #endif
97  ,
98  ValueType const& v)
99  {
100  assert(a.size() == i);
101  a.push_back(v);
102  }
103  };
104 
106  {
107  template <typename ContainerType>
108  static bool check_size(boost::type<ContainerType>, std::size_t sz)
109  {
110  return ContainerType::max_size() >= sz;
111  }
112  };
113 
115  {
116  template <typename ContainerType, typename ValueType>
117  static void
118  set_value(ContainerType& a, std::size_t /*i*/, ValueType const& v)
119  {
120  a.push_back(v);
121  }
122  };
123 
125  {
126  template <typename ContainerType, typename ValueType>
127  static void
128  set_value(ContainerType& a, std::size_t /*i*/, ValueType const& v)
129  {
130  a.insert(v);
131  }
132  };
133 
134  template <typename ContainerType, typename ConversionPolicy>
136  {
137  typedef typename ContainerType::value_type container_element_type;
138 
140  {
141  boost::python::converter::registry::push_back(
142  &convertible,
143  &construct,
144  boost::python::type_id<ContainerType>());
145  }
146 
147  static void* convertible(PyObject* obj_ptr)
148  {
149  if (!( PyList_Check(obj_ptr)
150  || PyTuple_Check(obj_ptr)
151  || PyIter_Check(obj_ptr)
152  || PyRange_Check(obj_ptr)
153  || ( !PyString_Check(obj_ptr)
154  && !PyUnicode_Check(obj_ptr)
155  && ( obj_ptr->ob_type == 0
156  || obj_ptr->ob_type->ob_type == 0
157  || obj_ptr->ob_type->ob_type->tp_name == 0
158  || std::strcmp(
159  obj_ptr->ob_type->ob_type->tp_name,
160  "Boost.Python.class") != 0)
161  && PyObject_HasAttrString(obj_ptr, "__len__")
162  && PyObject_HasAttrString(obj_ptr, "__getitem__")))) return 0;
163  boost::python::handle<> obj_iter(
164  boost::python::allow_null(PyObject_GetIter(obj_ptr)));
165  if (!obj_iter.get()) { // must be convertible to an iterator
166  PyErr_Clear();
167  return 0;
168  }
169  if (ConversionPolicy::check_convertibility_per_element()) {
170  int obj_size = PyObject_Length(obj_ptr);
171  if (obj_size < 0) { // must be a measurable sequence
172  PyErr_Clear();
173  return 0;
174  }
175  if (!ConversionPolicy::check_size(
176  boost::type<ContainerType>(), obj_size)) return 0;
177  bool is_range = PyRange_Check(obj_ptr);
178  std::size_t i=0;
179  if (!all_elements_convertible(obj_iter, is_range, i)) return 0;
180  if (!is_range) assert(i == (std::size_t)obj_size);
181  }
182  return obj_ptr;
183  }
184 
185  // This loop factored out by Achim Domma to avoid Visual C++
186  // Internal Compiler Error.
187  static bool
189  boost::python::handle<>& obj_iter,
190  bool is_range,
191  std::size_t& i)
192  {
193  for(;;i++) {
194  boost::python::handle<> py_elem_hdl(
195  boost::python::allow_null(PyIter_Next(obj_iter.get())));
196  if (PyErr_Occurred()) {
197  PyErr_Clear();
198  return false;
199  }
200  if (!py_elem_hdl.get()) break; // end of iteration
201  boost::python::object py_elem_obj(py_elem_hdl);
202  boost::python::extract<container_element_type>
203  elem_proxy(py_elem_obj);
204  if (!elem_proxy.check()) return false;
205  if (is_range) break; // in a range all elements are of the same type
206  }
207  return true;
208  }
209 
210  static void construct(
211  PyObject* obj_ptr,
212  boost::python::converter::rvalue_from_python_stage1_data* data)
213  {
214  boost::python::handle<> obj_iter(PyObject_GetIter(obj_ptr));
215  void* storage = (
216  (boost::python::converter::rvalue_from_python_storage<ContainerType>*)
217  data)->storage.bytes;
218  new (storage) ContainerType();
219  data->convertible = storage;
220  ContainerType& result = *((ContainerType*)storage);
221  std::size_t i=0;
222  for(;;i++) {
223  boost::python::handle<> py_elem_hdl(
224  boost::python::allow_null(PyIter_Next(obj_iter.get())));
225  if (PyErr_Occurred()) boost::python::throw_error_already_set();
226  if (!py_elem_hdl.get()) break; // end of iteration
227  boost::python::object py_elem_obj(py_elem_hdl);
228  boost::python::extract<container_element_type> elem_proxy(py_elem_obj);
229  ConversionPolicy::set_value(result, i, elem_proxy());
230  }
231  ConversionPolicy::assert_size(boost::type<ContainerType>(), i);
232  }
233  };
234 
235  template <typename ContainerType>
237  {
239  boost::python::to_python_converter<
240  ContainerType,
242 #ifdef BOOST_PYTHON_SUPPORTS_PY_SIGNATURES
243  , true
244 #endif
245  >();
246  }
247  };
248 
249  template <typename ContainerType, typename ConversionPolicy>
250  struct tuple_mapping : to_tuple_mapping<ContainerType>
251  {
254  ContainerType,
255  ConversionPolicy>();
256  }
257  };
258 
259  template <typename ContainerType>
261  {
264  ContainerType,
266  }
267  };
268 
269  template <typename ContainerType>
271  {
274  ContainerType,
276  }
277  };
278 
279  template <typename ContainerType>
281  {
284  ContainerType,
286  }
287  };
288 
289  template <typename ContainerType>
291  {
294  ContainerType,
295  set_policy>();
296  }
297  };
298 
299 }}} // namespace scitbx::boost_python::container_conversions
300 
301 #endif // SCITBX_BOOST_PYTHON_CONTAINER_CONVERSIONS_H