From 0e76fcf7069cadb55e6ef8f2d1c8bde899abd3b9 Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Thu, 14 Nov 2002 01:40:16 +0000 Subject: [PATCH] auto_ptr support [SVN r16228] --- .../python/converter/arg_from_python.hpp | 4 +- .../python/converter/arg_to_python_base.hpp | 1 - .../python/converter/return_from_python.hpp | 13 +++- .../converter/to_python_function_type.hpp | 29 +++++++- .../boost/python/object/class_converters.hpp | 2 - include/boost/python/object/class_wrapper.hpp | 17 ++++- .../boost/python/object/pointer_holder.hpp | 1 - include/boost/python/object/select_holder.hpp | 3 +- test/Jamfile | 20 +++--- test/auto_ptr.cpp | 70 +++++++++++++++++++ test/auto_ptr.py | 54 ++++++++++++++ test/test_builtin_converters.cpp | 24 +++++-- test/test_builtin_converters2.cpp | 10 +++ 13 files changed, 225 insertions(+), 23 deletions(-) create mode 100644 test/auto_ptr.cpp create mode 100644 test/auto_ptr.py create mode 100644 test/test_builtin_converters2.cpp diff --git a/include/boost/python/converter/arg_from_python.hpp b/include/boost/python/converter/arg_from_python.hpp index 1e3dfa26..78d00052 100755 --- a/include/boost/python/converter/arg_from_python.hpp +++ b/include/boost/python/converter/arg_from_python.hpp @@ -103,7 +103,9 @@ template struct arg_rvalue_from_python { typedef typename boost::add_reference< - typename boost::add_const::type + T + // We can't add_const here, or it would be impossible to pass + // auto_ptr args from Python to C++ >::type result_type; arg_rvalue_from_python(PyObject*); diff --git a/include/boost/python/converter/arg_to_python_base.hpp b/include/boost/python/converter/arg_to_python_base.hpp index 385a7839..850da036 100755 --- a/include/boost/python/converter/arg_to_python_base.hpp +++ b/include/boost/python/converter/arg_to_python_base.hpp @@ -5,7 +5,6 @@ // to its suitability for any purpose. #ifndef ARG_TO_PYTHON_BASE_DWA200237_HPP # define ARG_TO_PYTHON_BASE_DWA200237_HPP -# include # include # include diff --git a/include/boost/python/converter/return_from_python.hpp b/include/boost/python/converter/return_from_python.hpp index c487aa22..18c2d227 100755 --- a/include/boost/python/converter/return_from_python.hpp +++ b/include/boost/python/converter/return_from_python.hpp @@ -11,9 +11,11 @@ # include # include # include -# include # include # include +# include +# include +# include namespace boost { namespace python { namespace converter { @@ -38,7 +40,14 @@ namespace detail template struct return_rvalue_from_python { - typedef typename call_traits::param_type result_type; + typedef typename mpl::if_< + mpl::logical_and< + has_trivial_copy, mpl::bool_c<(sizeof(T) <= 2 * sizeof(double))> + > + , T + , T& + >::type result_type; + return_rvalue_from_python(); result_type operator()(PyObject*); private: diff --git a/include/boost/python/converter/to_python_function_type.hpp b/include/boost/python/converter/to_python_function_type.hpp index 197654e5..debad150 100644 --- a/include/boost/python/converter/to_python_function_type.hpp +++ b/include/boost/python/converter/to_python_function_type.hpp @@ -6,6 +6,7 @@ #ifndef TO_PYTHON_FUNCTION_TYPE_DWA200236_HPP # define TO_PYTHON_FUNCTION_TYPE_DWA200236_HPP # include +# include namespace boost { namespace python { namespace converter { @@ -19,9 +20,35 @@ typedef PyObject* (*to_python_function_t)(void const*); template struct as_to_python_function { + // Assertion functions used to prevent wrapping of converters + // which take non-const reference parameters. The T* argument in + // the first overload ensures it isn't used in case T is a + // reference. + template + static int convert_function_must_take_value_or_const_reference(U(*)(T), int, T* = 0); + template + static int convert_function_must_take_value_or_const_reference(U(*)(T const&), long ...); + static PyObject* convert(void const* x) { - return ToPython::convert(*(T const*)x); + + BOOST_STATIC_ASSERT( + sizeof( + convert_function_must_take_value_or_const_reference(&ToPython::convert, 1L)) + == sizeof(int)); + + // Yes, the const_cast below opens a hole in const-correctness, + // but it's needed to convert auto_ptr to python. + // + // How big a hole is it? It allows ToPython::convert() to be + // a function which modifies its argument. The upshot is that + // client converters applied to const objects may invoke + // undefined behavior. The damage, however, is limited by the + // use of the assertion function. Thus, the only way this can + // modify its argument is if T is an auto_ptr-like type. There + // is still a const-correctness hole w.r.t. auto_ptr const, + // but c'est la vie. + return ToPython::convert(*const_cast(static_cast(x))); } }; diff --git a/include/boost/python/object/class_converters.hpp b/include/boost/python/object/class_converters.hpp index 79119ef7..83a16ac8 100644 --- a/include/boost/python/object/class_converters.hpp +++ b/include/boost/python/object/class_converters.hpp @@ -6,9 +6,7 @@ #ifndef CLASS_CONVERTERS_DWA2002119_HPP # define CLASS_CONVERTERS_DWA2002119_HPP -# include # include -# include # include # include # include diff --git a/include/boost/python/object/class_wrapper.hpp b/include/boost/python/object/class_wrapper.hpp index 61364d9d..fe802989 100644 --- a/include/boost/python/object/class_wrapper.hpp +++ b/include/boost/python/object/class_wrapper.hpp @@ -13,14 +13,27 @@ namespace boost { namespace python { namespace objects { // Used to convert objects of type Src to wrapped C++ classes by // building a new instance object and installing a Holder constructed -// from the Src object. +// from a Src const&. template struct class_wrapper : to_python_converter > { static PyObject* convert(Src const& x) { - return MakeInstance::execute(cref(x)); + return MakeInstance::execute(boost::ref(x)); + } +}; + +// Used to convert objects of type Src to wrapped C++ classes by +// building a new instance object and installing a Holder constructed +// from a Src value. +template +struct class_value_wrapper + : to_python_converter > +{ + static PyObject* convert(Src x) + { + return MakeInstance::execute(x); } }; diff --git a/include/boost/python/object/pointer_holder.hpp b/include/boost/python/object/pointer_holder.hpp index d0196941..3a65dcb3 100644 --- a/include/boost/python/object/pointer_holder.hpp +++ b/include/boost/python/object/pointer_holder.hpp @@ -16,7 +16,6 @@ # include # include # include -# include # include # include # include diff --git a/include/boost/python/object/select_holder.hpp b/include/boost/python/object/select_holder.hpp index 3535fd29..c1396169 100644 --- a/include/boost/python/object/select_holder.hpp +++ b/include/boost/python/object/select_holder.hpp @@ -11,6 +11,7 @@ # include # include # include +# include # include # include # include @@ -97,7 +98,7 @@ namespace detail static inline void register_(mpl::bool_c) { python::detail::force_instantiate( - objects::class_wrapper< + objects::class_value_wrapper< Ptr , type , make_instance >()); diff --git a/test/Jamfile b/test/Jamfile index 4f554cc4..81f1f99a 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -55,15 +55,19 @@ rule bpl-test ( name ? : files * : requirements * ) boost-python-runtest $(name) : $(py) $(modules) ; } +bpl-test auto_ptr ; bpl-test minimal ; bpl-test args ; bpl-test numpy ; bpl-test enum ; -bpl-test docstring ; bpl-test exception_translator ; bpl-test pearu1 : test_cltree.py cltree.cpp ; bpl-test try : newtest.py m1.cpp m2.cpp ; -bpl-test builtin_converters : test_builtin_converters.py test_builtin_converters.cpp ; + +# Had to break this up into two files for MSVC6 +extension builtin_converters : test_builtin_converters.cpp test_builtin_converters2.cpp ../build/boost_python ; +boost-python-runtest builtin_converters : test_builtin_converters.py builtin_converters ; + bpl-test test_pointer_adoption ; bpl-test operators ; bpl-test callbacks ; @@ -112,6 +116,8 @@ bpl-test pickle3 ; bpl-test nested ; +bpl-test docstring ; + if $(TEST_BIENSTMAN_NON_BUGS) { bpl-test bienstman4 ; @@ -161,11 +167,9 @@ run select_arg_to_python_test.cpp ../src/converter/type_id.cpp ../../test/ : $(UNIT_TEST_PROPERTIES) ; -if $(TEST_EXPECTED_FAILURES) -{ - compile-fail ./raw_pyobject_fail1.cpp : $(PYTHON_PROPERTIES) ; - compile-fail ./raw_pyobject_fail2.cpp : $(PYTHON_PROPERTIES) ; - compile-fail ./object_fail1.cpp : $(PYTHON_PROPERTIES) ; -} +compile-fail ./raw_pyobject_fail1.cpp : $(PYTHON_PROPERTIES) ; +compile-fail ./raw_pyobject_fail2.cpp : $(PYTHON_PROPERTIES) ; +compile-fail ./as_to_python_function.cpp : $(PYTHON_PROPERTIES) ; +compile-fail ./object_fail1.cpp : $(PYTHON_PROPERTIES) ; diff --git a/test/auto_ptr.cpp b/test/auto_ptr.cpp new file mode 100644 index 00000000..39c3941b --- /dev/null +++ b/test/auto_ptr.cpp @@ -0,0 +1,70 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + +#include +#include +#include +#include +#include "test_class.hpp" + +#include + +using namespace boost::python; + +typedef test_class<> X; + +int look(std::auto_ptr const& x) +{ + return (x.get()) ? x->value() : -1; +} + +int steal(std::auto_ptr x) +{ + return x->value(); +} + +int maybe_steal(std::auto_ptr& x, bool doit) +{ + int n = x->value(); + if (doit) + x.release(); + return n; +} + +std::auto_ptr make() +{ + return std::auto_ptr(new X(77)); +} + +std::auto_ptr callback(object f) +{ + std::auto_ptr x(new X(77)); +// call(f.ptr(),x); +// return std::auto_ptr(new X(77)); + return call >(f.ptr(), x); +} + +std::auto_ptr extract_(object o) +{ + return extract >(o); +} + +BOOST_PYTHON_MODULE(auto_ptr_ext) +{ + class_, boost::noncopyable>("X", init()) + .def("value", &X::value) + ; + + def("look", look); + def("steal", steal); + def("maybe_steal", maybe_steal); + def("make", make); + def("callback", callback); + def("extract", extract_); +} + +#include "module_tail.cpp" + diff --git a/test/auto_ptr.py b/test/auto_ptr.py new file mode 100644 index 00000000..7c6ce51f --- /dev/null +++ b/test/auto_ptr.py @@ -0,0 +1,54 @@ +''' +>>> from auto_ptr_ext import * +>>> x = X(42) +>>> x.value() +42 + +>>> look(x), look(x) +(42, 42) + +>>> maybe_steal(x, 0) +42 +>>> look(x) +42 + +>>> maybe_steal(x, 1) +42 +>>> (not '--broken-auto-ptr' in sys.argv) and look(x) or -1 +-1 + +>>> x = X(69) +>>> steal(x) +69 +>>> (not '--broken-auto-ptr' in sys.argv) and look(x) or -1 +-1 + +>>> if not '--broken-auto-ptr' in sys.argv: +... try: x.value() +... except TypeError: pass +... else: print 'expected a TypeError exception' + +>>> x = make() +>>> look(x) +77 + +>>> y = callback(lambda y: y) +>>> y.value() +77 + +>>> extract(x).value() +77 +''' + +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 + sys.exit(run()[0]) diff --git a/test/test_builtin_converters.cpp b/test/test_builtin_converters.cpp index 917a468a..a078d41f 100644 --- a/test/test_builtin_converters.cpp +++ b/test/test_builtin_converters.cpp @@ -44,6 +44,10 @@ using boost::python::handle; using boost::python::object; using boost::python::borrowed; +// MSVC6 can't process this whole file at once, so we break it up into +// two parts. See test_builtin_converters2.cpp +#ifndef BOOST_PYTHON_WRAP_MORE_BUILTIN_CONVERTERS + // Used to test that arbitrary handle<>s can be returned handle get_type(handle<> x) { @@ -57,6 +61,8 @@ handle<> return_null_handle() char const* rewrap_value_mutable_cstring(char* x) { return x; } +void wrap_more(); + BOOST_PYTHON_MODULE(builtin_converters) { def("get_type", get_type); @@ -74,10 +80,10 @@ BOOST_PYTHON_MODULE(builtin_converters) def("rewrap_value_unsigned_long", by_value::rewrap); // using Python's macro instead of Boost's - we don't seem to get the // config right all the time. -#ifdef HAVE_LONG_LONG +# ifdef HAVE_LONG_LONG def("rewrap_value_long_long", by_value::rewrap); def("rewrap_value_unsigned_long_long", by_value::rewrap); -#endif +# endif def("rewrap_value_float", by_value::rewrap); def("rewrap_value_double", by_value::rewrap); def("rewrap_value_long_double", by_value::rewrap); @@ -92,6 +98,14 @@ BOOST_PYTHON_MODULE(builtin_converters) // Expose this to illustrate our failings ;-). See test_builtin_converters.py def("rewrap_value_mutable_cstring", rewrap_value_mutable_cstring); + wrap_more(); +} + +#else // BOOST_PYTHON_WRAP_MORE_BUILTIN_CONVERTERS -- this part + // compiled into test_builtin_converters2.cpp + +void wrap_more() +{ def("rewrap_const_reference_bool", by_const_reference::rewrap); def("rewrap_const_reference_char", by_const_reference::rewrap); @@ -105,10 +119,10 @@ BOOST_PYTHON_MODULE(builtin_converters) def("rewrap_const_reference_unsigned_long", by_const_reference::rewrap); // using Python's macro instead of Boost's - we don't seem to get the // config right all the time. -#ifdef HAVE_LONG_LONG +# ifdef HAVE_LONG_LONG def("rewrap_const_reference_long_long", by_const_reference::rewrap); def("rewrap_const_reference_unsigned_long_long", by_const_reference::rewrap); -#endif +# endif def("rewrap_const_reference_float", by_const_reference::rewrap); def("rewrap_const_reference_double", by_const_reference::rewrap); def("rewrap_const_reference_long_double", by_const_reference::rewrap); @@ -124,3 +138,5 @@ BOOST_PYTHON_MODULE(builtin_converters) def("rewrap_reference_object", by_reference::rewrap); } +#endif // BOOST_PYTHON_WRAP_MORE_BUILTIN_CONVERTERS + diff --git a/test/test_builtin_converters2.cpp b/test/test_builtin_converters2.cpp new file mode 100644 index 00000000..37807637 --- /dev/null +++ b/test/test_builtin_converters2.cpp @@ -0,0 +1,10 @@ +// Copyright David Abrahams 2002. Permission to copy, use, +// modify, sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + +// This file just defines the rest of the tests that MSVC6 can't +// compile unless we break the file up. It's a hack, I'll admit... +#define BOOST_PYTHON_WRAP_MORE_BUILTIN_CONVERTERS +#include "test_builtin_converters.cpp"