diff --git a/include/boost/python/tuple_converter.hpp b/include/boost/python/tuple_converter.hpp new file mode 100644 index 000000000..d390f6771 --- /dev/null +++ b/include/boost/python/tuple_converter.hpp @@ -0,0 +1,137 @@ +// Copyright Sander Kersten +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#ifndef TUPLE_CONVERTER_SPK20162802_HPP +# define TUPLE_CONVERTER_SPK20162802_HPP + +# include +# include +# include +# include + + +namespace boost { namespace python { + +namespace detail +{ + void tuple_to_python_(PyObject*, tuples::null_type, int) {} + + template + void tuple_to_python_(PyObject* obj, tuples::cons const& x, int N) + { + PyTuple_SET_ITEM(obj, N, incref(python::object(x.get_head()).ptr())); + tuple_to_python_(obj, x.get_tail(), N+1); + } + + template + PyObject* tuple_to_python(tuples::cons const& x) + { + PyObject *obj = PyTuple_New(tuples::length >::value); + tuple_to_python_(obj, x, 0); + return obj; + } +} + +template +struct tuple_to_python_converter +{ + static PyObject* convert(T const& x) + { + return detail::tuple_to_python(x); + } +}; + +namespace detail +{ + template + struct types_the_same; + + template + struct types_the_same + { + static bool same(PyObject*) { return false; } + }; + + template + struct types_the_same + { + static bool same(PyObject*) { return true; } + }; + + template + struct types_the_same > + { + static bool same(PyObject* tuple) + { + if (extract(PyTuple_GetItem(tuple, N)).check()) + return types_the_same::same(tuple); + else + return false; + } + }; + + void assign_tuple(const tuples::null_type&, PyObject*, int) {} + + template + void assign_tuple(tuples::cons& x, PyObject* tuple, int N = 0) + { + x.get_head() = extract(PyTuple_GetItem(tuple, N)); + assign_tuple(x.get_tail(), tuple, N+1); + } +} + +template +struct tuple_from_python_converter +{ + tuple_from_python_converter() + { + boost::python::converter::registry::push_back( + &convertible, + &construct, + type_id()); + } + + static void* convertible(PyObject* obj) + { + if (!PyTuple_Check(obj)) { + return 0; + } + if (PyTuple_Size(obj) != tuples::length::value) { + return 0; + } + if (!detail::types_the_same< + 0 + , tuples::length::value + , typename Tuple::inherited>::same(obj)) { + return 0; + } + return obj; + } + + static void construct( + PyObject* obj, + converter::rvalue_from_python_stage1_data* data) + { + void* storage = ( + (converter::rvalue_from_python_storage*) + data)->storage.bytes; + + Tuple *tuple = new (storage) Tuple(); + + detail::assign_tuple(*tuple, obj); + + data->convertible = storage; + } +}; + +template +void tuple_converter(const Tuple* = NULL) +{ + to_python_converter >(); + tuple_from_python_converter(); +} + +}} // namespace boost::python + +#endif // TUPLE_CONVERTER_SPK20162802_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 552418800..b9312bf3f 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -8,7 +8,7 @@ import os ; lib socket ; use-project /boost/python : ../build ; -project /boost/python/test +project /boost/python/test : requirements gcc:-Wextra qnxnto:socket @@ -27,7 +27,7 @@ rule py-run ( sources * : input-file ? ) : $(input-file) : #requirements BOOST_PYTHON_SUPPRESS_REGISTRY_INITIALIZATION - + ] ; } @@ -90,8 +90,8 @@ bpl-test crossmod_exception [ bpl-test andreas_beyer ] [ bpl-test wrapper_held_type ] -[ bpl-test polymorphism2_auto_ptr - : polymorphism2_auto_ptr.py polymorphism2.py polymorphism2_auto_ptr.cpp +[ bpl-test polymorphism2_auto_ptr + : polymorphism2_auto_ptr.py polymorphism2.py polymorphism2_auto_ptr.cpp ] [ bpl-test polymorphism ] @@ -109,7 +109,7 @@ bpl-test crossmod_exception [ bpl-test try : newtest.py m1.cpp m2.cpp ] [ bpl-test const_argument ] [ bpl-test keywords : keywords.cpp keywords_test.py ] - + [ python-extension builtin_converters_ext : test_builtin_converters.cpp /boost/python//boost_python ] [ bpl-test builtin_converters : test_builtin_converters.py builtin_converters_ext ] @@ -174,6 +174,8 @@ bpl-test crossmod_opaque [ bpl-test vector_indexing_suite ] +[ bpl-test tuple_converter ] + [ bpl-test pointer_vector : # files : # requirements @@ -181,13 +183,13 @@ bpl-test crossmod_opaque # Whenever the cause for the failure of the polymorphism test is found # and fixed, this should be retested. hp_cxx:no ] - + [ python-extension map_indexing_suite_ext : map_indexing_suite.cpp int_map_indexing_suite.cpp a_map_indexing_suite.cpp /boost/python//boost_python ] -[ bpl-test +[ bpl-test map_indexing_suite : map_indexing_suite.py map_indexing_suite_ext ] - + [ run import_.cpp /boost/python//boost_python $(PY) : : import_.py ] # if $(TEST_BIENSTMAN_NON_BUGS) @@ -215,11 +217,11 @@ bpl-test crossmod_opaque [ py-compile copy_ctor_mutates_rhs.cpp ] [ py-run upcast.cpp ] - + [ py-compile select_holder.cpp ] - -[ run select_from_python_test.cpp ../src/converter/type_id.cpp - : + +[ run select_from_python_test.cpp ../src/converter/type_id.cpp + : : : BOOST_PYTHON_STATIC_LIB $(PY) diff --git a/test/tuple_converter.cpp b/test/tuple_converter.cpp new file mode 100644 index 000000000..0c5ba0991 --- /dev/null +++ b/test/tuple_converter.cpp @@ -0,0 +1,32 @@ +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#include +#include +#include +#include +#include +#include + +using namespace boost::python; + +boost::tuple test_tuple_int(boost::tuple t) +{ + return t; +} + +boost::tuple test_tuple_int_string(boost::tuple t) +{ + return t; +} + +BOOST_PYTHON_MODULE(tuple_converter_ext) +{ + tuple_converter >(); + tuple_converter >(); + + def("test_tuple_int", test_tuple_int); + def("test_tuple_int_string", test_tuple_int_string); +} + +#include "module_tail.cpp" diff --git a/test/tuple_converter.py b/test/tuple_converter.py new file mode 100644 index 000000000..c0ab86c4e --- /dev/null +++ b/test/tuple_converter.py @@ -0,0 +1,37 @@ +# Distributed under the Boost Software License, Version 1.0. (See +# accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +""" +>>> from tuple_converter_ext import * +>>> test_tuple_int((3,)) +(3,) +>>> test_tuple_int_string((42, "Foo")) +(42, 'Foo') +>>> try: result = test_tuple_int((5,6)) +... except TypeError: pass +... else: print 'expected an exception, got', result, 'instead' + +>>> try: result = test_tuple_int_string((5,6)) +... except TypeError: pass +... else: print 'expected an exception, got', result, 'instead' + +>>> try: result = test_tuple_int_string((5,)) +... except TypeError: pass +... else: print 'expected an exception, got', result, 'instead' + +""" + +def run(args = None): + import sys + import doctest + + if args is not None: + sys.argv = args + return doctest.testmod(sys.modules.get(__name__)) + +if __name__ == '__main__': + print "running..." + import sys + status = run()[0] + if (status == 0): print "Done." + sys.exit(status)