1 /*************************************************************************** 2 * Copyright (C) 2009 by Francesco Biscani * 3 * bluescarni@gmail.com * 4 * * 5 * This program is free software; you can redistribute it and/or modify * 6 * it under the terms of the GNU General Public License as published by * 7 * the Free Software Foundation; either version 3 of the License, or * 8 * (at your option) any later version. * 9 * * 10 * This program is distributed in the hope that it will be useful, * 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 * GNU General Public License for more details. * 14 * * 15 * You should have received a copy of the GNU General Public License * 16 * along with this program; if not, write to the * 17 * Free Software Foundation, Inc., * 18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 19 ***************************************************************************/ 20 21 // Slightly adapted from: 22 // http://cctbx.svn.sourceforge.net/viewvc/cctbx/trunk/scitbx/boost_python/container_conversions.h 23 24 // Original license agreement follows. 25 26 /* 27 28 *** License agreement *** 29 30 cctbx Copyright (c) 2006, The Regents of the University of 31 California, through Lawrence Berkeley National Laboratory (subject to 32 receipt of any required approvals from the U.S. Dept. of Energy). All 33 rights reserved. 34 35 Redistribution and use in source and binary forms, with or without 36 modification, are permitted provided that the following conditions are met: 37 38 (1) Redistributions of source code must retain the above copyright 39 notice, this list of conditions and the following disclaimer. 40 41 (2) Redistributions in binary form must reproduce the above copyright 42 notice, this list of conditions and the following disclaimer in the 43 documentation and/or other materials provided with the distribution. 44 45 (3) Neither the name of the University of California, Lawrence Berkeley 46 National Laboratory, U.S. Dept. of Energy nor the names of its 47 contributors may be used to endorse or promote products derived from 48 this software without specific prior written permission. 49 50 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 51 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 52 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 53 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 54 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 55 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 56 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 57 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 58 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 59 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 60 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 61 62 You are under no obligation whatsoever to provide any bug fixes, 63 patches, or upgrades to the features, functionality or performance of 64 the source code ("Enhancements") to anyone; however, if you choose to 65 make your Enhancements available either publicly, or directly to 66 Lawrence Berkeley National Laboratory, without imposing a separate 67 written license agreement for such Enhancements, then you hereby grant 68 the following license: a non-exclusive, royalty-free perpetual license 69 to install, use, modify, prepare derivative works, incorporate into 70 other computer software, distribute, and sublicense such enhancements or 71 derivative works thereof, in binary and source code form. 72 73 */ 74 75 #ifndef BOOST_PYTHON_CONTAINER_CONVERSIONS_H 76 #define BOOST_PYTHON_CONTAINER_CONVERSIONS_H 77 78 #include <boost/python/extract.hpp> 79 #include <boost/python/list.hpp> 80 #include <boost/python/to_python_converter.hpp> 81 #include <boost/python/tuple.hpp> 82 83 template <typename ContainerType> 84 struct to_tuple { convertto_tuple85 static PyObject *convert(ContainerType const &a) 86 { 87 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(boost::python::object(*p)); 91 } 92 return boost::python::incref(boost::python::tuple(result).ptr()); 93 } 94 get_pytypeto_tuple95 static const PyTypeObject *get_pytype() 96 { 97 return &PyTuple_Type; 98 } 99 }; 100 101 struct default_policy { check_convertibility_per_elementdefault_policy102 static bool check_convertibility_per_element() 103 { 104 return false; 105 } 106 107 template <typename ContainerType> check_sizedefault_policy108 static bool check_size(boost::type<ContainerType>, std::size_t /*sz*/) 109 { 110 return true; 111 } 112 113 template <typename ContainerType> assert_sizedefault_policy114 static void assert_size(boost::type<ContainerType>, std::size_t /*sz*/) 115 { 116 } 117 118 template <typename ContainerType> reservedefault_policy119 static void reserve(ContainerType &a, std::size_t sz) 120 { 121 } 122 }; 123 124 struct fixed_size_policy { check_convertibility_per_elementfixed_size_policy125 static bool check_convertibility_per_element() 126 { 127 return true; 128 } 129 130 template <typename ContainerType> check_sizefixed_size_policy131 static bool check_size(boost::type<ContainerType>, std::size_t sz) 132 { 133 return std::tuple_size<ContainerType>::value == sz; 134 } 135 136 template <typename ContainerType> assert_sizefixed_size_policy137 static void assert_size(boost::type<ContainerType>, std::size_t sz) 138 { 139 if (!check_size(boost::type<ContainerType>(), sz)) { 140 PyErr_SetString(PyExc_RuntimeError, "Insufficient elements for fixed-size array."); 141 boost::python::throw_error_already_set(); 142 } 143 } 144 145 template <typename ContainerType> reservefixed_size_policy146 static void reserve(ContainerType & /*a*/, std::size_t sz) 147 { 148 if (sz > std::tuple_size<ContainerType>::value) { 149 PyErr_SetString(PyExc_RuntimeError, "Too many elements for fixed-size array."); 150 boost::python::throw_error_already_set(); 151 } 152 } 153 154 template <typename ContainerType, typename ValueType> set_valuefixed_size_policy155 static void set_value(ContainerType &a, std::size_t i, ValueType const &v) 156 { 157 reserve(a, i + 1); 158 a[i] = v; 159 } 160 }; 161 162 struct variable_capacity_policy : default_policy { 163 template <typename ContainerType> reservevariable_capacity_policy164 static void reserve(ContainerType &a, std::size_t sz) 165 { 166 a.reserve(sz); 167 } 168 169 template <typename ContainerType, typename ValueType> set_valuevariable_capacity_policy170 static void set_value(ContainerType &a, std::size_t 171 #if !defined(NDEBUG) 172 i 173 #endif 174 , 175 ValueType const &v) 176 { 177 assert(a.size() == i); 178 a.push_back(v); 179 } 180 }; 181 182 struct fixed_capacity_policy : variable_capacity_policy { 183 template <typename ContainerType> check_sizefixed_capacity_policy184 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 template <typename ContainerType, typename ValueType> set_valuelinked_list_policy192 static void set_value(ContainerType &a, std::size_t /*i*/, ValueType const &v) 193 { 194 a.push_back(v); 195 } 196 }; 197 198 struct set_policy : default_policy { 199 template <typename ContainerType, typename ValueType> set_valueset_policy200 static void set_value(ContainerType &a, std::size_t /*i*/, ValueType const &v) 201 { 202 a.insert(v); 203 } 204 }; 205 206 template <typename ContainerType, typename ConversionPolicy> 207 struct from_python_sequence { 208 typedef typename ContainerType::value_type container_element_type; 209 from_python_sequencefrom_python_sequence210 from_python_sequence() 211 { 212 boost::python::converter::registry::push_back(&convertible, &construct, 213 boost::python::type_id<ContainerType>()); 214 } 215 convertiblefrom_python_sequence216 static void *convertible(PyObject *obj_ptr) 217 { 218 if (!(PyList_Check(obj_ptr) || PyTuple_Check(obj_ptr) || PyIter_Check(obj_ptr) || PyRange_Check(obj_ptr) 219 || (!PyBytes_Check(obj_ptr) && !PyUnicode_Check(obj_ptr) 220 && (obj_ptr->ob_type == 0 || Py_TYPE(obj_ptr->ob_type) == 0 || Py_TYPE(obj_ptr->ob_type)->tp_name == 0 221 || std::strcmp(Py_TYPE(obj_ptr->ob_type)->tp_name, "Boost.Python.class") != 0) 222 && PyObject_HasAttrString(obj_ptr, "__len__") && PyObject_HasAttrString(obj_ptr, "__getitem__")))) 223 return 0; 224 boost::python::handle<> obj_iter(boost::python::allow_null(PyObject_GetIter(obj_ptr))); 225 if (!obj_iter.get()) { // must be convertible to an iterator 226 PyErr_Clear(); 227 return 0; 228 } 229 if (ConversionPolicy::check_convertibility_per_element()) { 230 int obj_size = PyObject_Length(obj_ptr); 231 if (obj_size < 0) { // must be a measurable sequence 232 PyErr_Clear(); 233 return 0; 234 } 235 if (!ConversionPolicy::check_size(boost::type<ContainerType>(), obj_size)) return 0; 236 bool is_range = PyRange_Check(obj_ptr); 237 std::size_t i = 0; 238 if (!all_elements_convertible(obj_iter, is_range, i)) return 0; 239 if (!is_range) assert(i == (size_t)obj_size); 240 } 241 return obj_ptr; 242 } 243 244 // This loop factored out by Achim Domma to avoid Visual C++ 245 // Internal Compiler Error. all_elements_convertiblefrom_python_sequence246 static bool all_elements_convertible(boost::python::handle<> &obj_iter, bool is_range, std::size_t &i) 247 { 248 for (;; i++) { 249 boost::python::handle<> py_elem_hdl(boost::python::allow_null(PyIter_Next(obj_iter.get()))); 250 if (PyErr_Occurred()) { 251 PyErr_Clear(); 252 return false; 253 } 254 if (!py_elem_hdl.get()) break; // end of iteration 255 boost::python::object py_elem_obj(py_elem_hdl); 256 boost::python::extract<container_element_type> elem_proxy(py_elem_obj); 257 if (!elem_proxy.check()) return false; 258 if (is_range) break; // in a range all elements are of the same type 259 } 260 return true; 261 } 262 constructfrom_python_sequence263 static void construct(PyObject *obj_ptr, boost::python::converter::rvalue_from_python_stage1_data *data) 264 { 265 boost::python::handle<> obj_iter(PyObject_GetIter(obj_ptr)); 266 void *storage = ((boost::python::converter::rvalue_from_python_storage<ContainerType> *)data)->storage.bytes; 267 new (storage) ContainerType(); 268 data->convertible = storage; 269 ContainerType &result = *((ContainerType *)storage); 270 std::size_t i = 0; 271 for (;; i++) { 272 boost::python::handle<> py_elem_hdl(boost::python::allow_null(PyIter_Next(obj_iter.get()))); 273 if (PyErr_Occurred()) boost::python::throw_error_already_set(); 274 if (!py_elem_hdl.get()) break; // end of iteration 275 boost::python::object py_elem_obj(py_elem_hdl); 276 boost::python::extract<container_element_type> elem_proxy(py_elem_obj); 277 ConversionPolicy::set_value(result, i, elem_proxy()); 278 } 279 ConversionPolicy::assert_size(boost::type<ContainerType>(), i); 280 } 281 }; 282 283 template <typename ContainerType> 284 struct to_tuple_mapping { to_tuple_mappingto_tuple_mapping285 to_tuple_mapping() 286 { 287 boost::python::to_python_converter<ContainerType, to_tuple<ContainerType> 288 #ifdef BOOST_PYTHON_SUPPORTS_PY_SIGNATURES 289 , 290 true 291 #endif 292 >(); 293 } 294 }; 295 296 template <typename ContainerType, typename ConversionPolicy> 297 struct tuple_mapping : to_tuple_mapping<ContainerType> { tuple_mappingtuple_mapping298 tuple_mapping() 299 { 300 from_python_sequence<ContainerType, ConversionPolicy>(); 301 } 302 }; 303 304 template <typename ContainerType> 305 struct tuple_mapping_fixed_size { tuple_mapping_fixed_sizetuple_mapping_fixed_size306 tuple_mapping_fixed_size() 307 { 308 tuple_mapping<ContainerType, fixed_size_policy>(); 309 } 310 }; 311 312 template <typename ContainerType> 313 struct tuple_mapping_fixed_capacity { tuple_mapping_fixed_capacitytuple_mapping_fixed_capacity314 tuple_mapping_fixed_capacity() 315 { 316 tuple_mapping<ContainerType, fixed_capacity_policy>(); 317 } 318 }; 319 320 template <typename ContainerType> 321 struct tuple_mapping_variable_capacity { tuple_mapping_variable_capacitytuple_mapping_variable_capacity322 tuple_mapping_variable_capacity() 323 { 324 tuple_mapping<ContainerType, variable_capacity_policy>(); 325 } 326 }; 327 328 template <typename ContainerType> 329 struct tuple_mapping_set { tuple_mapping_settuple_mapping_set330 tuple_mapping_set() 331 { 332 tuple_mapping<ContainerType, set_policy>(); 333 } 334 }; 335 336 #endif 337