HDK
 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 terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_TF_PY_CONTAINER_CONVERSIONS_H
8 #define PXR_BASE_TF_PY_CONTAINER_CONVERSIONS_H
9 
10 /// \file tf/pyContainerConversions.h
11 /// Utilities for providing C++ <-> Python container support.
12 
13 /*
14  * Adapted (modified) from original at http://cctbx.sourceforge.net
15  * Original file:
16  * cctbx/scitbx/include/scitbx/boost_python/container_conversions.h
17  * License:
18  * http://cvs.sourceforge.net/viewcvs.py/cctbx/cctbx/
19  * LICENSE.txt?rev=1.2&view=markup
20  */
21 
22 #include "pxr/pxr.h"
23 
24 #include "pxr/base/tf/refPtr.h"
25 #include "pxr/base/tf/weakPtr.h"
26 #include "pxr/base/tf/diagnostic.h"
27 #include "pxr/base/tf/iterator.h"
28 #include "pxr/base/tf/pyUtils.h"
29 
30 #include "pxr/external/boost/python/list.hpp"
31 #include "pxr/external/boost/python/tuple.hpp"
32 #include "pxr/external/boost/python/extract.hpp"
33 #include "pxr/external/boost/python/to_python_converter.hpp"
34 
35 #include <deque>
36 #include <list>
37 #include <set>
38 #include <utility>
39 #include <vector>
40 
42 
43 // Converts any iterable C++ container to a Python list.
44 template <typename ContainerType>
46 {
47  static PyObject* convert(ContainerType const &c)
48  {
49  pxr_boost::python::list result;
50  TF_FOR_ALL(i, c) {
51  result.append(*i);
52  }
53  return pxr_boost::python::incref(result.ptr());
54  }
55 };
56 
57 // Converts any iterable C++ container to a Python set.
58 template <typename ContainerType>
60 {
61  static PyObject* convert(ContainerType const &c)
62  {
63  PyObject* result = PySet_New(nullptr);
64  for (const auto &elem : c) {
65  PySet_Add(result, pxr_boost::python::object(elem).ptr());
66  }
67  return result;
68  }
69 };
70 
71 template <typename ContainerType>
73 {
74  static PyObject* convert(ContainerType const &c)
75  {
76  return pxr_boost::python::incref(TfPyCopyMapToDictionary(c).ptr());
77  }
78 };
79 
80 namespace TfPyContainerConversions {
81 
82  template <typename ContainerType>
83  struct to_tuple
84  {
85  static PyObject* convert(ContainerType const& a)
86  {
87  pxr_boost::python::list result;
88  typedef typename ContainerType::const_iterator const_iter;
89  for(const_iter p=a.begin();p!=a.end();p++) {
90  result.append(pxr_boost::python::object(*p));
91  }
92  return pxr_boost::python::incref(pxr_boost::python::tuple(result).ptr());
93  }
94  };
95 
96  template <typename First, typename Second>
97  struct to_tuple<std::pair<First, Second> > {
98  static PyObject* convert(std::pair<First, Second> const& a)
99  {
100  pxr_boost::python::tuple result =
101  pxr_boost::python::make_tuple(a.first, a.second);
102  return pxr_boost::python::incref(result.ptr());
103  }
104  };
105 
106  template <typename... T>
107  struct to_tuple<std::tuple<T...>> {
108  static PyObject* convert(std::tuple<T...> const& a)
109  {
110  return std::apply(
111  [](T const&... v) {
112  pxr_boost::python::tuple result =
113  pxr_boost::python::make_tuple(v...);
114  return pxr_boost::python::incref(result.ptr());
115  }, a);
116  }
117  };
118 
120  {
121  static bool check_convertibility_per_element() { return false; }
122 
123  template <typename ContainerType>
124  static bool check_size(ContainerType*, std::size_t sz)
125  {
126  return true;
127  }
128 
129  template <typename ContainerType>
130  static void assert_size(ContainerType*, std::size_t sz) {}
131 
132  template <typename ContainerType>
133  static void reserve(ContainerType& a, std::size_t sz) {}
134  };
135 
137  {
138  static bool check_convertibility_per_element() { return true; }
139 
140  template <typename ContainerType>
141  static bool check_size(ContainerType*, std::size_t sz)
142  {
143  return ContainerType::size() == sz;
144  }
145 
146  template <typename ContainerType>
147  static void assert_size(ContainerType* c, std::size_t sz)
148  {
149  if (!check_size(c, sz)) {
150  PyErr_SetString(PyExc_RuntimeError,
151  "Insufficient elements for fixed-size array.");
152  pxr_boost::python::throw_error_already_set();
153  }
154  }
155 
156  template <typename ContainerType>
157  static void reserve(ContainerType& a, std::size_t sz)
158  {
159  if (sz > ContainerType::size()) {
160  PyErr_SetString(PyExc_RuntimeError,
161  "Too many elements for fixed-size array.");
162  pxr_boost::python::throw_error_already_set();
163  }
164  }
165 
166  template <typename ContainerType, typename ValueType>
167  static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
168  {
169  reserve(a, i+1);
170  a[i] = v;
171  }
172  };
173 
175  {
176  template <typename ContainerType>
177  static void reserve(ContainerType& a, std::size_t sz)
178  {
179  a.reserve(sz);
180  }
181 
182  template <typename ContainerType, typename ValueType>
183  static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
184  {
185  TF_AXIOM(a.size() == i);
186  a.push_back(v);
187  }
188  };
189 
191  {
192  static bool check_convertibility_per_element() { return true; }
193  };
194 
196  {
197  template <typename ContainerType>
198  static bool check_size(ContainerType*, std::size_t sz)
199  {
200  return ContainerType::max_size() >= sz;
201  }
202  };
203 
205  {
206  template <typename ContainerType, typename ValueType>
207  static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
208  {
209  a.push_back(v);
210  }
211  };
212 
214  {
215  template <typename ContainerType, typename ValueType>
216  static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
217  {
218  a.insert(v);
219  }
220  };
221 
222  template <typename ContainerType, typename ConversionPolicy>
224  {
226 
228  {
229  pxr_boost::python::converter::registry::push_back(
230  &convertible,
231  &construct,
232  pxr_boost::python::type_id<ContainerType>());
233  }
234 
235  static void* convertible(PyObject* obj_ptr)
236  {
237  if (!( PyList_Check(obj_ptr)
238  || PyTuple_Check(obj_ptr)
239  || PySet_Check(obj_ptr)
240  || PyFrozenSet_Check(obj_ptr)
241  || PyIter_Check(obj_ptr)
242  || PyRange_Check(obj_ptr)
243  || ( !PyBytes_Check(obj_ptr)
244  && !PyUnicode_Check(obj_ptr)
245  && ( Py_TYPE(obj_ptr) == 0
246  || Py_TYPE(Py_TYPE(obj_ptr)) == 0
247  || Py_TYPE(Py_TYPE(obj_ptr))->tp_name == 0
248  || std::strcmp(
249  Py_TYPE(Py_TYPE(obj_ptr))->tp_name,
250  "Boost.Python.class") != 0)
251  && PyObject_HasAttrString(obj_ptr, "__len__")
252  && PyObject_HasAttrString(obj_ptr, "__getitem__")))) return 0;
253  pxr_boost::python::handle<> obj_iter(
254  pxr_boost::python::allow_null(PyObject_GetIter(obj_ptr)));
255  if (!obj_iter.get()) { // must be convertible to an iterator
256  PyErr_Clear();
257  return 0;
258  }
259  if (ConversionPolicy::check_convertibility_per_element()) {
260  Py_ssize_t obj_size = PyObject_Length(obj_ptr);
261  if (obj_size < 0) { // must be a measurable sequence
262  PyErr_Clear();
263  return 0;
264  }
265  if (!ConversionPolicy::check_size(
266  (ContainerType*)nullptr, obj_size)) return 0;
267  bool is_range = PyRange_Check(obj_ptr);
268  std::size_t i=0;
269  if (!all_elements_convertible(obj_iter, is_range, i)) return 0;
270  if (!is_range) assert(i == (std::size_t)obj_size);
271  }
272  return obj_ptr;
273  }
274 
275  // This loop factored out by Achim Domma to avoid Visual C++
276  // Internal Compiler Error.
277  static bool
279  pxr_boost::python::handle<>& obj_iter,
280  bool is_range,
281  std::size_t& i)
282  {
283  for(;;i++) {
284  pxr_boost::python::handle<> py_elem_hdl(
285  pxr_boost::python::allow_null(PyIter_Next(obj_iter.get())));
286  if (PyErr_Occurred()) {
287  PyErr_Clear();
288  return false;
289  }
290  if (!py_elem_hdl.get()) break; // end of iteration
291  pxr_boost::python::object py_elem_obj(py_elem_hdl);
292  pxr_boost::python::extract<container_element_type>
293  elem_proxy(py_elem_obj);
294  if (!elem_proxy.check()) return false;
295  if (is_range) break; // in a range all elements are of the same type
296  }
297  return true;
298  }
299 
300  static void construct(
301  PyObject* obj_ptr,
302  pxr_boost::python::converter::rvalue_from_python_stage1_data* data)
303  {
304  pxr_boost::python::handle<> obj_iter(PyObject_GetIter(obj_ptr));
305  void* storage = (
306  (pxr_boost::python::converter::rvalue_from_python_storage<ContainerType>*)
307  data)->storage.bytes;
308  new (storage) ContainerType();
309  data->convertible = storage;
310  ContainerType& result = *((ContainerType*)storage);
311  std::size_t i=0;
312  for(;;i++) {
313  pxr_boost::python::handle<> py_elem_hdl(
314  pxr_boost::python::allow_null(PyIter_Next(obj_iter.get())));
315  if (PyErr_Occurred()) pxr_boost::python::throw_error_already_set();
316  if (!py_elem_hdl.get()) break; // end of iteration
317  pxr_boost::python::object py_elem_obj(py_elem_hdl);
318  pxr_boost::python::extract<container_element_type> elem_proxy(py_elem_obj);
319  ConversionPolicy::set_value(result, i, elem_proxy());
320  }
321  ConversionPolicy::assert_size((ContainerType*)nullptr, i);
322  }
323  };
324 
325  template <typename Indexes, typename TupleType, typename... T>
327 
328  template <size_t... I, typename TupleType, typename... T>
329  struct from_python_tuple_impl<std::index_sequence<I...>, TupleType, T...>
330  {
332  {
333  pxr_boost::python::converter::registry::push_back(
334  &convertible,
335  &construct,
336  pxr_boost::python::type_id<TupleType>());
337  }
338 
339  static void* convertible(PyObject* obj_ptr)
340  {
341  if (!PyTuple_Check(obj_ptr) || PyTuple_Size(obj_ptr) != sizeof...(T)) {
342  return 0;
343  }
344  if ((!pxr_boost::python::extract<T>(
345  PyTuple_GetItem(obj_ptr, I)).check() || ...)) {
346  return 0;
347  }
348  return obj_ptr;
349  }
350 
351  static void construct(
352  PyObject* obj_ptr,
353  pxr_boost::python::converter::rvalue_from_python_stage1_data* data)
354  {
355  void* storage = (
356  (pxr_boost::python::converter::rvalue_from_python_storage<TupleType>*)
357  data)->storage.bytes;
358  new (storage) TupleType(
359  pxr_boost::python::extract<T>(PyTuple_GetItem(obj_ptr, I))()...);
360  data->convertible = storage;
361  }
362  };
363 
364  template <typename PairType>
367  std::make_index_sequence<2>, PairType,
368  typename PairType::first_type, typename PairType::second_type
369  >
370  {
371  };
372 
373  template <typename TupleType>
375 
376  template <typename... T>
377  struct from_python_tuple<std::tuple<T...>>
379  std::index_sequence_for<T...>, std::tuple<T...>, T...
380  >
381  {
382  };
383 
384  template <typename ContainerType>
386  {
388  pxr_boost::python::to_python_converter<
391  }
392  };
393 
394  template <typename ContainerType, typename ConversionPolicy>
395  struct tuple_mapping : to_tuple_mapping<ContainerType>
396  {
400  ConversionPolicy>();
401  }
402  };
403 
404  template <typename ContainerType>
406  {
411  }
412  };
413 
414  template <typename ContainerType>
416  {
421  }
422  };
423 
424  template <typename ContainerType>
426  {
431  }
432  };
433 
434  template <typename ContainerType>
436  {
440  set_policy>();
441  }
442  };
443 
444  template <typename ContainerType>
446  {
448  pxr_boost::python::to_python_converter<
452  }
453  };
454 
455 } // namespace TfPyContainerConversions
456 
457 template <class T>
459 {
460  using namespace TfPyContainerConversions;
467 }
468 
470 
471 #endif // PXR_BASE_TF_PY_CONTAINER_CONVERSIONS_H
static bool check_size(ContainerType *, std::size_t sz)
static void construct(PyObject *obj_ptr, pxr_boost::python::converter::rvalue_from_python_stage1_data *data)
static bool all_elements_convertible(pxr_boost::python::handle<> &obj_iter, bool is_range, std::size_t &i)
static void set_value(ContainerType &a, std::size_t i, ValueType const &v)
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
const GLdouble * v
Definition: glcorearb.h:837
static PyObject * convert(std::tuple< T...> const &a)
static bool check_size(ContainerType *, std::size_t sz)
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
static PyObject * convert(ContainerType const &c)
static void set_value(ContainerType &a, std::size_t i, ValueType const &v)
**But if you need a result
Definition: thread.h:622
static void set_value(ContainerType &a, std::size_t i, ValueType const &v)
uint64 value_type
Definition: GA_PrimCompat.h:29
static PyObject * convert(ContainerType const &a)
void TfPyRegisterStlSequencesFromPython()
static bool check_size(ContainerType *, std::size_t sz)
static void reserve(ContainerType &a, std::size_t sz)
static void assert_size(ContainerType *c, std::size_t sz)
GLsizeiptr size
Definition: glcorearb.h:664
#define TF_AXIOM(cond)
static PyObject * convert(ContainerType const &c)
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
auto ptr(T p) -> const void *
Definition: format.h:4331
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
static void assert_size(ContainerType *, std::size_t sz)
static void set_value(ContainerType &a, std::size_t i, ValueType const &v)
static void construct(PyObject *obj_ptr, pxr_boost::python::converter::rvalue_from_python_stage1_data *data)
#define TF_FOR_ALL(iter, c)
Definition: iterator.h:373
static void reserve(ContainerType &a, std::size_t sz)
static PyObject * convert(std::pair< First, Second > const &a)
static PyObject * convert(ContainerType const &c)
bool ValueType
Definition: NanoVDB.h:5729
Definition: format.h:1821
pxr_boost::python::dict TfPyCopyMapToDictionary(Map const &map)
Creates a python dictionary from a std::map.
Definition: pyUtils.h:258
static void reserve(ContainerType &a, std::size_t sz)