All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules 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
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"
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
41PXR_NAMESPACE_OPEN_SCOPE
42
43// Converts any iterable C++ container to a Python list.
44template <typename ContainerType>
45struct TfPySequenceToPython
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.
58template <typename ContainerType>
59struct TfPySequenceToPythonSet
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
71template <typename ContainerType>
72struct TfPyMapToPythonDict
73{
74 static PyObject* convert(ContainerType const &c)
75 {
76 return pxr_boost::python::incref(TfPyCopyMapToDictionary(c).ptr());
77 }
78};
79
80namespace 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
119 struct default_policy
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
136 struct fixed_size_policy
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
174 struct variable_capacity_policy : default_policy
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
190 struct variable_capacity_all_items_convertible_policy : variable_capacity_policy
191 {
192 static bool check_convertibility_per_element() { return true; }
193 };
194
195 struct fixed_capacity_policy : variable_capacity_policy
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
204 struct linked_list_policy : default_policy
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
213 struct set_policy : default_policy
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>
223 struct from_python_sequence
224 {
225 typedef typename ContainerType::value_type container_element_type;
226
227 from_python_sequence()
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
278 all_elements_convertible(
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>
326 struct from_python_tuple_impl;
327
328 template <size_t... I, typename TupleType, typename... T>
329 struct from_python_tuple_impl<std::index_sequence<I...>, TupleType, T...>
330 {
331 from_python_tuple_impl()
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>
365 struct from_python_tuple_pair
366 : from_python_tuple_impl<
367 std::make_index_sequence<2>, PairType,
368 typename PairType::first_type, typename PairType::second_type
369 >
370 {
371 };
372
373 template <typename TupleType>
374 struct from_python_tuple;
375
376 template <typename... T>
377 struct from_python_tuple<std::tuple<T...>>
378 : from_python_tuple_impl<
379 std::index_sequence_for<T...>, std::tuple<T...>, T...
380 >
381 {
382 };
383
384 template <typename ContainerType>
385 struct to_tuple_mapping
386 {
387 to_tuple_mapping() {
388 pxr_boost::python::to_python_converter<
389 ContainerType,
390 to_tuple<ContainerType> >();
391 }
392 };
393
394 template <typename ContainerType, typename ConversionPolicy>
395 struct tuple_mapping : to_tuple_mapping<ContainerType>
396 {
397 tuple_mapping() {
398 from_python_sequence<
399 ContainerType,
400 ConversionPolicy>();
401 }
402 };
403
404 template <typename ContainerType>
405 struct tuple_mapping_fixed_size
406 {
407 tuple_mapping_fixed_size() {
408 tuple_mapping<
409 ContainerType,
410 fixed_size_policy>();
411 }
412 };
413
414 template <typename ContainerType>
415 struct tuple_mapping_fixed_capacity
416 {
417 tuple_mapping_fixed_capacity() {
418 tuple_mapping<
419 ContainerType,
420 fixed_capacity_policy>();
421 }
422 };
423
424 template <typename ContainerType>
425 struct tuple_mapping_variable_capacity
426 {
427 tuple_mapping_variable_capacity() {
428 tuple_mapping<
429 ContainerType,
430 variable_capacity_policy>();
431 }
432 };
433
434 template <typename ContainerType>
435 struct tuple_mapping_set
436 {
437 tuple_mapping_set() {
438 tuple_mapping<
439 ContainerType,
440 set_policy>();
441 }
442 };
443
444 template <typename ContainerType>
445 struct tuple_mapping_pair
446 {
447 tuple_mapping_pair() {
448 pxr_boost::python::to_python_converter<
449 ContainerType,
450 to_tuple<ContainerType> >();
451 from_python_tuple_pair<ContainerType>();
452 }
453 };
454
455} // namespace TfPyContainerConversions
456
457template <class T>
458void TfPyRegisterStlSequencesFromPython()
459{
460 using namespace TfPyContainerConversions;
461 from_python_sequence<
462 std::vector<T>, variable_capacity_all_items_convertible_policy>();
463 from_python_sequence<
464 std::list<T>, variable_capacity_all_items_convertible_policy>();
465 from_python_sequence<
466 std::deque<T>, variable_capacity_all_items_convertible_policy>();
467}
468
469PXR_NAMESPACE_CLOSE_SCOPE
470
471#endif // PXR_BASE_TF_PY_CONTAINER_CONVERSIONS_H
Low-level utilities for informing users of various internal and external diagnostic conditions.
A simple iterator adapter for STL containers.
Miscellaneous Utilities for dealing with script.
pxr_boost::python::dict TfPyCopyMapToDictionary(Map const &map)
Creates a python dictionary from a std::map.
Definition: pyUtils.h:258
#define TF_FOR_ALL(iter, c)
Macro for iterating over a container.
Definition: iterator.h:373
#define TF_AXIOM(cond)
Aborts if the condition cond is not met.
Definition: diagnostic.h:193
STL namespace.
Reference counting.
Pointer storage with deletion detection.