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