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