Loading...
Searching...
No Matches
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 <boost/python/list.hpp>
31#include <boost/python/tuple.hpp>
32#include <boost/python/extract.hpp>
33#include <boost/python/to_python_converter.hpp>
34
35#include <deque>
36#include <list>
37#include <set>
38#include <vector>
39
40PXR_NAMESPACE_OPEN_SCOPE
41
42// Converts any iterable C++ container to a Python list.
43template <typename ContainerType>
44struct TfPySequenceToPython
45{
46 static PyObject* convert(ContainerType const &c)
47 {
48 boost::python::list result;
49 TF_FOR_ALL(i, c) {
50 result.append(*i);
51 }
52 return boost::python::incref(result.ptr());
53 }
54};
55
56// Converts any iterable C++ container to a Python set.
57template <typename ContainerType>
58struct TfPySequenceToPythonSet
59{
60 static PyObject* convert(ContainerType const &c)
61 {
62 PyObject* result = PySet_New(nullptr);
63 for (const auto &elem : c) {
64 PySet_Add(result, boost::python::object(elem).ptr());
65 }
66 return result;
67 }
68};
69
70template <typename ContainerType>
71struct TfPyMapToPythonDict
72{
73 static PyObject* convert(ContainerType const &c)
74 {
75 return boost::python::incref(TfPyCopyMapToDictionary(c).ptr());
76 }
77};
78
79namespace TfPyContainerConversions {
80
81 template <typename ContainerType>
82 struct to_tuple
83 {
84 static PyObject* convert(ContainerType const& a)
85 {
86 boost::python::list result;
87 typedef typename ContainerType::const_iterator const_iter;
88 for(const_iter p=a.begin();p!=a.end();p++) {
89 result.append(boost::python::object(*p));
90 }
91 return boost::python::incref(boost::python::tuple(result).ptr());
92 }
93 };
94
95 template <typename First, typename Second>
96 struct to_tuple<std::pair<First, Second> > {
97 static PyObject* convert(std::pair<First, Second> const& a)
98 {
99 boost::python::tuple result =
100 boost::python::make_tuple(a.first, a.second);
101 return boost::python::incref(result.ptr());
102 }
103 };
104
105 struct default_policy
106 {
107 static bool check_convertibility_per_element() { return false; }
108
109 template <typename ContainerType>
110 static bool check_size(boost::type<ContainerType>, std::size_t sz)
111 {
112 return true;
113 }
114
115 template <typename ContainerType>
116 static void assert_size(boost::type<ContainerType>, std::size_t sz) {}
117
118 template <typename ContainerType>
119 static void reserve(ContainerType& a, std::size_t sz) {}
120 };
121
122 struct fixed_size_policy
123 {
124 static bool check_convertibility_per_element() { return true; }
125
126 template <typename ContainerType>
127 static bool check_size(boost::type<ContainerType>, std::size_t sz)
128 {
129 return ContainerType::size() == sz;
130 }
131
132 template <typename ContainerType>
133 static void assert_size(boost::type<ContainerType>, std::size_t sz)
134 {
135 if (!check_size(boost::type<ContainerType>(), sz)) {
136 PyErr_SetString(PyExc_RuntimeError,
137 "Insufficient elements for fixed-size array.");
138 boost::python::throw_error_already_set();
139 }
140 }
141
142 template <typename ContainerType>
143 static void reserve(ContainerType& a, std::size_t sz)
144 {
145 if (sz > ContainerType::size()) {
146 PyErr_SetString(PyExc_RuntimeError,
147 "Too many elements for fixed-size array.");
148 boost::python::throw_error_already_set();
149 }
150 }
151
152 template <typename ContainerType, typename ValueType>
153 static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
154 {
155 reserve(a, i+1);
156 a[i] = v;
157 }
158 };
159
160 struct variable_capacity_policy : default_policy
161 {
162 template <typename ContainerType>
163 static void reserve(ContainerType& a, std::size_t sz)
164 {
165 a.reserve(sz);
166 }
167
168 template <typename ContainerType, typename ValueType>
169 static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
170 {
171 TF_AXIOM(a.size() == i);
172 a.push_back(v);
173 }
174 };
175
176 struct variable_capacity_all_items_convertible_policy : variable_capacity_policy
177 {
178 static bool check_convertibility_per_element() { return true; }
179 };
180
181 struct fixed_capacity_policy : variable_capacity_policy
182 {
183 template <typename ContainerType>
184 static bool check_size(boost::type<ContainerType>, std::size_t sz)
185 {
186 return ContainerType::max_size() >= sz;
187 }
188 };
189
190 struct linked_list_policy : default_policy
191 {
192 template <typename ContainerType, typename ValueType>
193 static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
194 {
195 a.push_back(v);
196 }
197 };
198
199 struct set_policy : default_policy
200 {
201 template <typename ContainerType, typename ValueType>
202 static void set_value(ContainerType& a, std::size_t i, ValueType const& v)
203 {
204 a.insert(v);
205 }
206 };
207
208 template <typename ContainerType, typename ConversionPolicy>
209 struct from_python_sequence
210 {
211 typedef typename ContainerType::value_type container_element_type;
212
213 from_python_sequence()
214 {
215 boost::python::converter::registry::push_back(
216 &convertible,
217 &construct,
218 boost::python::type_id<ContainerType>());
219 }
220
221 static void* convertible(PyObject* obj_ptr)
222 {
223 if (!( PyList_Check(obj_ptr)
224 || PyTuple_Check(obj_ptr)
225 || PySet_Check(obj_ptr)
226 || PyFrozenSet_Check(obj_ptr)
227 || PyIter_Check(obj_ptr)
228 || PyRange_Check(obj_ptr)
229 || ( !PyBytes_Check(obj_ptr)
230 && !PyUnicode_Check(obj_ptr)
231 && ( Py_TYPE(obj_ptr) == 0
232 || Py_TYPE(Py_TYPE(obj_ptr)) == 0
233 || Py_TYPE(Py_TYPE(obj_ptr))->tp_name == 0
234 || std::strcmp(
235 Py_TYPE(Py_TYPE(obj_ptr))->tp_name,
236 "Boost.Python.class") != 0)
237 && PyObject_HasAttrString(obj_ptr, "__len__")
238 && PyObject_HasAttrString(obj_ptr, "__getitem__")))) return 0;
239 boost::python::handle<> obj_iter(
240 boost::python::allow_null(PyObject_GetIter(obj_ptr)));
241 if (!obj_iter.get()) { // must be convertible to an iterator
242 PyErr_Clear();
243 return 0;
244 }
245 if (ConversionPolicy::check_convertibility_per_element()) {
246 Py_ssize_t obj_size = PyObject_Length(obj_ptr);
247 if (obj_size < 0) { // must be a measurable sequence
248 PyErr_Clear();
249 return 0;
250 }
251 if (!ConversionPolicy::check_size(
252 boost::type<ContainerType>(), obj_size)) return 0;
253 bool is_range = PyRange_Check(obj_ptr);
254 std::size_t i=0;
255 if (!all_elements_convertible(obj_iter, is_range, i)) return 0;
256 if (!is_range) assert(i == (std::size_t)obj_size);
257 }
258 return obj_ptr;
259 }
260
261 // This loop factored out by Achim Domma to avoid Visual C++
262 // Internal Compiler Error.
263 static bool
264 all_elements_convertible(
265 boost::python::handle<>& obj_iter,
266 bool is_range,
267 std::size_t& i)
268 {
269 for(;;i++) {
270 boost::python::handle<> py_elem_hdl(
271 boost::python::allow_null(PyIter_Next(obj_iter.get())));
272 if (PyErr_Occurred()) {
273 PyErr_Clear();
274 return false;
275 }
276 if (!py_elem_hdl.get()) break; // end of iteration
277 boost::python::object py_elem_obj(py_elem_hdl);
278 boost::python::extract<container_element_type>
279 elem_proxy(py_elem_obj);
280 if (!elem_proxy.check()) return false;
281 if (is_range) break; // in a range all elements are of the same type
282 }
283 return true;
284 }
285
286 static void construct(
287 PyObject* obj_ptr,
288 boost::python::converter::rvalue_from_python_stage1_data* data)
289 {
290 boost::python::handle<> obj_iter(PyObject_GetIter(obj_ptr));
291 void* storage = (
292 (boost::python::converter::rvalue_from_python_storage<ContainerType>*)
293 data)->storage.bytes;
294 new (storage) ContainerType();
295 data->convertible = storage;
296 ContainerType& result = *((ContainerType*)storage);
297 std::size_t i=0;
298 for(;;i++) {
299 boost::python::handle<> py_elem_hdl(
300 boost::python::allow_null(PyIter_Next(obj_iter.get())));
301 if (PyErr_Occurred()) boost::python::throw_error_already_set();
302 if (!py_elem_hdl.get()) break; // end of iteration
303 boost::python::object py_elem_obj(py_elem_hdl);
304 boost::python::extract<container_element_type> elem_proxy(py_elem_obj);
305 ConversionPolicy::set_value(result, i, elem_proxy());
306 }
307 ConversionPolicy::assert_size(boost::type<ContainerType>(), i);
308 }
309 };
310
311 template <typename PairType>
312 struct from_python_tuple_pair {
313 typedef typename PairType::first_type first_type;
314 typedef typename PairType::second_type second_type;
315
316 from_python_tuple_pair()
317 {
318 boost::python::converter::registry::push_back(
319 &convertible,
320 &construct,
321 boost::python::type_id<PairType>());
322 }
323
324 static void* convertible(PyObject* obj_ptr)
325 {
326 if (!PyTuple_Check(obj_ptr) || PyTuple_Size(obj_ptr) != 2) {
327 return 0;
328 }
329 boost::python::extract<first_type> e1(PyTuple_GetItem(obj_ptr, 0));
330 boost::python::extract<second_type> e2(PyTuple_GetItem(obj_ptr, 1));
331 if (!e1.check() || !e2.check()) {
332 return 0;
333 }
334 return obj_ptr;
335 }
336
337 static void construct(
338 PyObject* obj_ptr,
339 boost::python::converter::rvalue_from_python_stage1_data* data)
340 {
341 void* storage = (
342 (boost::python::converter::rvalue_from_python_storage<PairType>*)
343 data)->storage.bytes;
344 boost::python::extract<first_type> e1(PyTuple_GetItem(obj_ptr, 0));
345 boost::python::extract<second_type> e2(PyTuple_GetItem(obj_ptr, 1));
346 new (storage) PairType(e1(), e2());
347 data->convertible = storage;
348 }
349 };
350
351 template <typename ContainerType>
352 struct to_tuple_mapping
353 {
354 to_tuple_mapping() {
355 boost::python::to_python_converter<
356 ContainerType,
357 to_tuple<ContainerType> >();
358 }
359 };
360
361 template <typename ContainerType, typename ConversionPolicy>
362 struct tuple_mapping : to_tuple_mapping<ContainerType>
363 {
364 tuple_mapping() {
365 from_python_sequence<
366 ContainerType,
367 ConversionPolicy>();
368 }
369 };
370
371 template <typename ContainerType>
372 struct tuple_mapping_fixed_size
373 {
374 tuple_mapping_fixed_size() {
375 tuple_mapping<
376 ContainerType,
377 fixed_size_policy>();
378 }
379 };
380
381 template <typename ContainerType>
382 struct tuple_mapping_fixed_capacity
383 {
384 tuple_mapping_fixed_capacity() {
385 tuple_mapping<
386 ContainerType,
387 fixed_capacity_policy>();
388 }
389 };
390
391 template <typename ContainerType>
392 struct tuple_mapping_variable_capacity
393 {
394 tuple_mapping_variable_capacity() {
395 tuple_mapping<
396 ContainerType,
397 variable_capacity_policy>();
398 }
399 };
400
401 template <typename ContainerType>
402 struct tuple_mapping_set
403 {
404 tuple_mapping_set() {
405 tuple_mapping<
406 ContainerType,
407 set_policy>();
408 }
409 };
410
411 template <typename ContainerType>
412 struct tuple_mapping_pair
413 {
414 tuple_mapping_pair() {
415 boost::python::to_python_converter<
416 ContainerType,
417 to_tuple<ContainerType> >();
418 from_python_tuple_pair<ContainerType>();
419 }
420 };
421
422} // namespace TfPyContainerConversions
423
424template <class T>
425void TfPyRegisterStlSequencesFromPython()
426{
427 using namespace TfPyContainerConversions;
428 from_python_sequence<
429 std::vector<T>, variable_capacity_all_items_convertible_policy>();
430 from_python_sequence<
431 std::list<T>, variable_capacity_all_items_convertible_policy>();
432 from_python_sequence<
433 std::deque<T>, variable_capacity_all_items_convertible_policy>();
434}
435
436PXR_NAMESPACE_CLOSE_SCOPE
437
438#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.
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.