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 27 /// \file tf/pyContainerConversions.h 28 /// Utilities for providing C++ <-> Python container support. 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 { convertTfPySequenceToPython63 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 { convertTfPyMapToPythonDict76 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 { convertto_tuple87 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 || ( !PyBytes_Check(obj_ptr) 233 && !PyUnicode_Check(obj_ptr) 234 && ( Py_TYPE(obj_ptr) == 0 235 || Py_TYPE(Py_TYPE(obj_ptr)) == 0 236 || Py_TYPE(Py_TYPE(obj_ptr))->tp_name == 0 237 || std::strcmp( 238 Py_TYPE(Py_TYPE(obj_ptr))->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 // PXR_BASE_TF_PY_CONTAINER_CONVERSIONS_H 442