All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyContainerConversions.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef TF_PYCONTAINERCONVERSIONS_H
25 #define TF_PYCONTAINERCONVERSIONS_H
26 
29 
30 /*
31  * Adapted (modified) from original at http://cctbx.sourceforge.net
32  * Original file:
33  * cctbx/scitbx/include/scitbx/boost_python/container_conversions.h
34  * License:
35  * http://cvs.sourceforge.net/viewcvs.py/cctbx/cctbx/
36  * LICENSE.txt?rev=1.2&view=markup
37  */
38 
39 #include "pxr/pxr.h"
40 
41 #include "pxr/base/tf/refPtr.h"
42 #include "pxr/base/tf/weakPtr.h"
43 #include "pxr/base/tf/diagnostic.h"
44 #include "pxr/base/tf/iterator.h"
45 #include "pxr/base/tf/pyUtils.h"
46 
47 #include <boost/python/list.hpp>
48 #include <boost/python/tuple.hpp>
49 #include <boost/python/extract.hpp>
50 #include <boost/python/to_python_converter.hpp>
51 
52 #include <deque>
53 #include <list>
54 #include <set>
55 #include <vector>
56 
57 PXR_NAMESPACE_OPEN_SCOPE
58 
59 // Converter from vector<string> to python list.
60 template <typename ContainerType>
61 struct TfPySequenceToPython
62 {
63  static PyObject* convert(ContainerType const &c)
64  {
65  boost::python::list result;
66  TF_FOR_ALL(i, c) {
67  result.append(*i);
68  }
69  return boost::python::incref(result.ptr());
70  }
71 };
72 
73 template <typename ContainerType>
74 struct TfPyMapToPythonDict
75 {
76  static PyObject* convert(ContainerType const &c)
77  {
78  return boost::python::incref(TfPyCopyMapToDictionary(c).ptr());
79  }
80 };
81 
82 namespace TfPyContainerConversions {
83 
84  template <typename ContainerType>
85  struct to_tuple
86  {
87  static PyObject* convert(ContainerType const& a)
88  {
89  boost::python::list result;
90  typedef typename ContainerType::const_iterator const_iter;
91  for(const_iter p=a.begin();p!=a.end();p++) {
92  result.append(boost::python::object(*p));
93  }
94  return boost::python::incref(boost::python::tuple(result).ptr());
95  }
96  };
97 
98  template <typename First, typename Second>
99  struct to_tuple<std::pair<First, Second> > {
100  static PyObject* convert(std::pair<First, Second> const& a)
101  {
102  boost::python::tuple result =
103  boost::python::make_tuple(a.first, a.second);
104  return boost::python::incref(result.ptr());
105  }
106  };
107 
108  struct default_policy
109  {
110  static bool check_convertibility_per_element() { return false; }
111 
112  template <typename ContainerType>
113  static bool check_size(boost::type<ContainerType>, std::size_t sz)
114  {
115  return true;
116  }
117 
118  template <typename ContainerType>
119  static void assert_size(boost::type<ContainerType>, std::size_t sz) {}
120 
121  template <typename ContainerType>
122  static void reserve(ContainerType& a, std::size_t sz) {}
123  };
124 
125  struct fixed_size_policy
126  {
127  static bool check_convertibility_per_element() { return true; }
128 
129  template <typename ContainerType>
130  static bool check_size(boost::type<ContainerType>, std::size_t sz)
131  {
132  return ContainerType::size() == sz;
133  }
134 
135  template <typename ContainerType>
136  static void assert_size(boost::type<ContainerType>, std::size_t sz)
137  {
138  if (!check_size(boost::type<ContainerType>(), sz)) {
139  PyErr_SetString(PyExc_RuntimeError,
140  "Insufficient elements for fixed-size array.");
141  boost::python::throw_error_already_set();
142  }
143  }
144 
145  template <typename ContainerType>
146  static void reserve(ContainerType& a, std::size_t sz)
147  {
148  if (sz > ContainerType::size()) {
149  PyErr_SetString(PyExc_RuntimeError,
150  "Too many elements for fixed-size array.");
151  boost::python::throw_error_already_set();
152  }
153  }
154 
155  template <typename ContainerType, typename ValueType>
156  static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
157  {
158  reserve(a, i+1);
159  a[i] = v;
160  }
161  };
162 
163  struct variable_capacity_policy : default_policy
164  {
165  template <typename ContainerType>
166  static void reserve(ContainerType& a, std::size_t sz)
167  {
168  a.reserve(sz);
169  }
170 
171  template <typename ContainerType, typename ValueType>
172  static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
173  {
174  TF_AXIOM(a.size() == i);
175  a.push_back(v);
176  }
177  };
178 
179  struct variable_capacity_all_items_convertible_policy : variable_capacity_policy
180  {
181  static bool check_convertibility_per_element() { return true; }
182  };
183 
184  struct fixed_capacity_policy : variable_capacity_policy
185  {
186  template <typename ContainerType>
187  static bool check_size(boost::type<ContainerType>, std::size_t sz)
188  {
189  return ContainerType::max_size() >= sz;
190  }
191  };
192 
193  struct linked_list_policy : default_policy
194  {
195  template <typename ContainerType, typename ValueType>
196  static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
197  {
198  a.push_back(v);
199  }
200  };
201 
202  struct set_policy : default_policy
203  {
204  template <typename ContainerType, typename ValueType>
205  static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
206  {
207  a.insert(v);
208  }
209  };
210 
211  template <typename ContainerType, typename ConversionPolicy>
212  struct from_python_sequence
213  {
214  typedef typename ContainerType::value_type container_element_type;
215 
216  from_python_sequence()
217  {
218  boost::python::converter::registry::push_back(
219  &convertible,
220  &construct,
221  boost::python::type_id<ContainerType>());
222  }
223 
224  static void* convertible(PyObject* obj_ptr)
225  {
226  if (!( PyList_Check(obj_ptr)
227  || PyTuple_Check(obj_ptr)
228  || PySet_Check(obj_ptr)
229  || PyFrozenSet_Check(obj_ptr)
230  || PyIter_Check(obj_ptr)
231  || PyRange_Check(obj_ptr)
232  || ( !PyString_Check(obj_ptr)
233  && !PyUnicode_Check(obj_ptr)
234  && ( obj_ptr->ob_type == 0
235  || obj_ptr->ob_type->ob_type == 0
236  || obj_ptr->ob_type->ob_type->tp_name == 0
237  || std::strcmp(
238  obj_ptr->ob_type->ob_type->tp_name,
239  "Boost.Python.class") != 0)
240  && PyObject_HasAttrString(obj_ptr, "__len__")
241  && PyObject_HasAttrString(obj_ptr, "__getitem__")))) return 0;
242  boost::python::handle<> obj_iter(
243  boost::python::allow_null(PyObject_GetIter(obj_ptr)));
244  if (!obj_iter.get()) { // must be convertible to an iterator
245  PyErr_Clear();
246  return 0;
247  }
248  if (ConversionPolicy::check_convertibility_per_element()) {
249  Py_ssize_t obj_size = PyObject_Length(obj_ptr);
250  if (obj_size < 0) { // must be a measurable sequence
251  PyErr_Clear();
252  return 0;
253  }
254  if (!ConversionPolicy::check_size(
255  boost::type<ContainerType>(), obj_size)) return 0;
256  bool is_range = PyRange_Check(obj_ptr);
257  std::size_t i=0;
258  if (!all_elements_convertible(obj_iter, is_range, i)) return 0;
259  if (!is_range) assert(i == (std::size_t)obj_size);
260  }
261  return obj_ptr;
262  }
263 
264  // This loop factored out by Achim Domma to avoid Visual C++
265  // Internal Compiler Error.
266  static bool
267  all_elements_convertible(
268  boost::python::handle<>& obj_iter,
269  bool is_range,
270  std::size_t& i)
271  {
272  for(;;i++) {
273  boost::python::handle<> py_elem_hdl(
274  boost::python::allow_null(PyIter_Next(obj_iter.get())));
275  if (PyErr_Occurred()) {
276  PyErr_Clear();
277  return false;
278  }
279  if (!py_elem_hdl.get()) break; // end of iteration
280  boost::python::object py_elem_obj(py_elem_hdl);
281  boost::python::extract<container_element_type>
282  elem_proxy(py_elem_obj);
283  if (!elem_proxy.check()) return false;
284  if (is_range) break; // in a range all elements are of the same type
285  }
286  return true;
287  }
288 
289  static void construct(
290  PyObject* obj_ptr,
291  boost::python::converter::rvalue_from_python_stage1_data* data)
292  {
293  boost::python::handle<> obj_iter(PyObject_GetIter(obj_ptr));
294  void* storage = (
295  (boost::python::converter::rvalue_from_python_storage<ContainerType>*)
296  data)->storage.bytes;
297  new (storage) ContainerType();
298  data->convertible = storage;
299  ContainerType& result = *((ContainerType*)storage);
300  std::size_t i=0;
301  for(;;i++) {
302  boost::python::handle<> py_elem_hdl(
303  boost::python::allow_null(PyIter_Next(obj_iter.get())));
304  if (PyErr_Occurred()) boost::python::throw_error_already_set();
305  if (!py_elem_hdl.get()) break; // end of iteration
306  boost::python::object py_elem_obj(py_elem_hdl);
307  boost::python::extract<container_element_type> elem_proxy(py_elem_obj);
308  ConversionPolicy::set_value(result, i, elem_proxy());
309  }
310  ConversionPolicy::assert_size(boost::type<ContainerType>(), i);
311  }
312  };
313 
314  template <typename PairType>
315  struct from_python_tuple_pair {
316  typedef typename PairType::first_type first_type;
317  typedef typename PairType::second_type second_type;
318 
319  from_python_tuple_pair()
320  {
321  boost::python::converter::registry::push_back(
322  &convertible,
323  &construct,
324  boost::python::type_id<PairType>());
325  }
326 
327  static void* convertible(PyObject* obj_ptr)
328  {
329  if (!PyTuple_Check(obj_ptr) || PyTuple_Size(obj_ptr) != 2) {
330  return 0;
331  }
332  boost::python::extract<first_type> e1(PyTuple_GetItem(obj_ptr, 0));
333  boost::python::extract<second_type> e2(PyTuple_GetItem(obj_ptr, 1));
334  if (!e1.check() || !e2.check()) {
335  return 0;
336  }
337  return obj_ptr;
338  }
339 
340  static void construct(
341  PyObject* obj_ptr,
342  boost::python::converter::rvalue_from_python_stage1_data* data)
343  {
344  void* storage = (
345  (boost::python::converter::rvalue_from_python_storage<PairType>*)
346  data)->storage.bytes;
347  boost::python::extract<first_type> e1(PyTuple_GetItem(obj_ptr, 0));
348  boost::python::extract<second_type> e2(PyTuple_GetItem(obj_ptr, 1));
349  new (storage) PairType(e1(), e2());
350  data->convertible = storage;
351  }
352  };
353 
354  template <typename ContainerType>
355  struct to_tuple_mapping
356  {
357  to_tuple_mapping() {
358  boost::python::to_python_converter<
359  ContainerType,
360  to_tuple<ContainerType> >();
361  }
362  };
363 
364  template <typename ContainerType, typename ConversionPolicy>
365  struct tuple_mapping : to_tuple_mapping<ContainerType>
366  {
367  tuple_mapping() {
368  from_python_sequence<
369  ContainerType,
370  ConversionPolicy>();
371  }
372  };
373 
374  template <typename ContainerType>
375  struct tuple_mapping_fixed_size
376  {
377  tuple_mapping_fixed_size() {
378  tuple_mapping<
379  ContainerType,
380  fixed_size_policy>();
381  }
382  };
383 
384  template <typename ContainerType>
385  struct tuple_mapping_fixed_capacity
386  {
387  tuple_mapping_fixed_capacity() {
388  tuple_mapping<
389  ContainerType,
390  fixed_capacity_policy>();
391  }
392  };
393 
394  template <typename ContainerType>
395  struct tuple_mapping_variable_capacity
396  {
397  tuple_mapping_variable_capacity() {
398  tuple_mapping<
399  ContainerType,
400  variable_capacity_policy>();
401  }
402  };
403 
404  template <typename ContainerType>
405  struct tuple_mapping_set
406  {
407  tuple_mapping_set() {
408  tuple_mapping<
409  ContainerType,
410  set_policy>();
411  }
412  };
413 
414  template <typename ContainerType>
415  struct tuple_mapping_pair
416  {
417  tuple_mapping_pair() {
418  boost::python::to_python_converter<
419  ContainerType,
420  to_tuple<ContainerType> >();
421  from_python_tuple_pair<ContainerType>();
422  }
423  };
424 
425 } // namespace TfPyContainerConversions
426 
427 template <class T>
428 void TfPyRegisterStlSequencesFromPython()
429 {
430  using namespace TfPyContainerConversions;
431  from_python_sequence<
432  std::vector<T>, variable_capacity_all_items_convertible_policy>();
433  from_python_sequence<
434  std::list<T>, variable_capacity_all_items_convertible_policy>();
435  from_python_sequence<
436  std::deque<T>, variable_capacity_all_items_convertible_policy>();
437 }
438 
439 PXR_NAMESPACE_CLOSE_SCOPE
440 
441 #endif // TF_PYCONTAINERCONVERSIONS_H
boost::python::dict TfPyCopyMapToDictionary(Map const &map)
Creates a python dictionary from a std::map.
Definition: pyUtils.h:231
#define TF_FOR_ALL(iter, c)
Macro for iterating over a container.
Definition: iterator.h:390
#define TF_AXIOM(cond)
Aborts if the condition cond is not met.
Definition: diagnostic.h:215