Patch for boost.python: Added return value policy return_pointee_value

Hi! I've created a patch that adds a new return value policy to boost.python: return_pointee_value. Roman Yakovenko has "invented" it, I have added it to the current SVN trunk on my PC. I've written documentation for it and also some test cases. David Abrahams encouraged me to send a patch to the boost list, which I do now. There are some issues with the patch (since its my first time, I'm doing something like that): - The test cases are not integrated into the Jamfile in the tests-directory (I dont know bjam very well). - I dont know, wether the tests are OK the way they are done. - I did not add an #include directive to any boost-python header. But some header should include the <boost/python/return_pointee_value.hpp>, but I dont know which one. Ok, that's it for now. I would aprreciate any comments or critic. Max Index: boost/python/return_pointee_value.hpp =================================================================== --- boost/python/return_pointee_value.hpp (revision 0) +++ boost/python/return_pointee_value.hpp (revision 0) @@ -0,0 +1,69 @@ +// Copyright Roman Yakovenko, Maximilian Matthe 2006, 2008. +// 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) +/* + * Generic return value policy for functions returning pointers which should be + * exposed as values. Can be used to return pointers to non-exposed types. + * + * The code is adapted from + * http://mail.python.org/pipermail/c++-sig/2006-November/011568.html . + */ +#ifndef RETURN_POINTEE_VALUE_9_11_2006 +#define RETURN_POINTEE_VALUE_9_11_2006 + +# include <boost/python/detail/prefix.hpp> +# include <boost/python/detail/indirect_traits.hpp> +# include <boost/python/object.hpp> +# include <boost/mpl/if.hpp> +# include <boost/python/to_python_indirect.hpp> +# include <boost/type_traits/composite_traits.hpp> + + +namespace boost{ namespace python{ + namespace detail{ + struct make_value_holder + { + template <class T> + static PyObject* execute(T* p) + { + if (p == 0) + { + return python::detail::none(); + } + else + { + object p_value( *p ); + return incref( p_value.ptr() ); + } + } + }; + + template <class R> + struct reference_existing_object_requires_a_pointer_return_type +# if defined(__GNUC__) && __GNUC__ >= 3 || defined(__EDG__) + {} +# endif + ; + + } //detail + + struct return_pointee_value + { + template <class T> + struct apply + { + BOOST_STATIC_CONSTANT( bool, ok = is_pointer<T>::value ); + + typedef typename mpl::if_c< + ok + , to_python_indirect<T, detail::make_value_holder> + , detail::reference_existing_object_requires_a_pointer_return_type<T> + >::type type; + }; + }; + + +} } //boost::python + +#endif//RETURN_POINTEE_VALUE_9_11_2006 \ No newline at end of file Index: libs/python/doc/v2/reference.html =================================================================== --- libs/python/doc/v2/reference.html (revision 47690) +++ libs/python/doc/v2/reference.html (working copy) @@ -877,7 +877,25 @@ </dd> </dl> </dd> + + <dt><a href= + "return_pointee_value.html">return_pointee_value.hpp</a></dt> + <dd> + <dl class="index"> + <dt><a href= + "return_pointee_value.html#classes">Classes</a></dt> + + <dd> + <dl class="index"> + <dt><a href= + "return_pointee_value.html#return_pointee_value-spec"> + return_pointee_value</a></dt> + </dl> + </dd> + </dl> + </dd> + <dt><a href="return_by_value.html">return_by_value.hpp</a></dt> <dd> Index: libs/python/doc/v2/reference_existing_object.html =================================================================== --- libs/python/doc/v2/reference_existing_object.html (revision 47690) +++ libs/python/doc/v2/reference_existing_object.html (working copy) @@ -80,6 +80,7 @@ "with_custodian_and_ward.html#with_custodian_and_ward-spec">with_custodian_and_ward</a>. This class is used in the implementation of <a href= "return_internal_reference.html#return_internal_reference-spec">return_internal_reference</a>.</p> + <p>This return value policy can only be used with types that were exposed with boost.python before.</p> <h4><a name="reference_existing_object-spec-synopsis"></a>Class <code>reference_existing_object</code> synopsis</h4> Index: libs/python/doc/v2/return_pointee_value.html =================================================================== --- libs/python/doc/v2/return_pointee_value.html (revision 0) +++ libs/python/doc/v2/return_pointee_value.html (revision 0) @@ -0,0 +1,241 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> + +<!-- Copyright Maximilian Matthe 2008. 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) --> +<html> + <head> + <meta name="generator" content= + "HTML Tidy for Windows (vers 1st August 2002), see www.w3.org"> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <link rel="stylesheet" type="text/css" href="../boost.css"> + + <title>Boost.Python - + <boost/python/return_pointee_value.hpp></title> + </head> + + <body> + <table border="0" cellpadding="7" cellspacing="0" width="100%" summary= + "header"> + <tr> + <td valign="top" width="300"> + <h3><a href="../../../../index.htm"><img height="86" width="277" + alt="C++ Boost" src="../../../../boost.png" border="0"></a></h3> + </td> + + <td valign="top"> + <h1 align="center"><a href="../index.html">Boost.Python</a></h1> + + <h2 align="center">Header + <boost/python/return_pointee_value.hpp></h2> + </td> + </tr> + </table> + <hr> + + <h2>Contents</h2> + + <dl class="page-index"> + <dt><a href="#classes">Classes</a></dt> + + <dd> + <dl class="page-index"> + <dt><a href="#return_pointee_value-spec">Class + <code>return_pointee_value</code></a></dt> + + <dd> + <dl class="page-index"> + <dt><a href="#return_pointee_value-spec-synopsis">Class + <code>return_pointee_value</code> synopsis</a></dt> + + <dt><a href= + "#return_pointee_value-spec-metafunctions">Class + <code>return_pointee_value</code> metafunctions</a></dt> + </dl> + </dd> + </dl> + </dd> + + <dt><a href="#examples">Example</a></dt> + </dl> + <hr> + + <h2><a name="classes"></a>Classes</h2> + + <h3><a name="return_pointee_value-spec"></a>Class + <code>return_pointee_value</code></h3> + + <p><code>return_pointee_value</code> is a model of <a href= + "ResultConverter.html#ResultConverterGenerator-concept">ResultConverterGenerator</a> + which can be used to wrap C++ functions, that return a pointer to a C++ object. The + policy implements the following logic: + + <pre> +if return value is NULL pointer: + return None +else: + return bp::object( * return value ) + </pre> + + It passes the value of the pointee to python, thus the conversion for T is used, if + return value is of type T*.</p> + <p>This return_value_policy can therefore be used to return pointers to python, which + types are not known to boost.python but only a conversion for the pointees. Therefore this + policy should be used to return pointers to objects whose types were wrapped with + other tools, such as SWIG (see example).</p> + + <p><b>Please note:</b> This policy does not take ownership of the wrapped pointer. If the + object pointed to is deleted in C++, the python-object will become invalid too, if your custom + conversion depends on the original object.</p> + + <h4><a name="return_pointee_value-spec-synopsis"></a>Class + <code>return_pointee_value</code> synopsis</h4> +<pre> +namespace boost { namespace python +{ + struct return_pointee_value + { + template <class T> struct apply; + }; +}} +</pre> + + <h4><a name="return_pointee_value-spec-metafunctions"></a>Class + <code>return_pointee_value</code> metafunctions</h4> +<pre> +template <class T> struct apply +</pre> + + <dl class="metafunction-semantics"> + <dt><b>Requires:</b> <code>T</code> is <code>U*</code>for some <code>U</code>.</dt> + + <dt><b>Returns:</b> <code>typedef <a href= + "to_python_indirect.html#to_python_indirect-spec">to_python_indirect</a><T,V> + type</code>, where <code>V</code> is a class whose + static <code>execute</code> function constructs a <code>boost::python::object</code> from + the dereferenced parameter of type <code>U*</code>.</dt> + </dl> + + <h2><a name="examples"></a>Example</h2> + <p><b>Example 1:</b> This example demonstrates the trivial use of <code>return_pointee_value</code> for returning + the value that the returned pointer points to: + </p> + <p>In C++:</p> + <pre> +#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/return_pointee_value.hpp> +#include <boost/python/return_value_policy.hpp> +#inlcude <wxPython.h> +#include <utility> + +// The function which is going to be wrapped: +int* get_value() +{ + static int val = 42; + return &val; +} + +// Function that returns a NULL-Pointer +int* get_null_value() +{ + return NULL; +} + +// The wrapper +BOOST_PYTHON_MODULE(example) +{ + using namespace boost::python; + // Wrap the functions with return_pointee_value + def("get_value", get_value, return_value_policy<return_pointee_value>()); + def("get_null_value", get_null_value, return_value_policy<return_pointee_value>()); +} + </pre> + <p>In Python:</p> + <pre> +>>> import example +>>> f = example.get_value() +>>> print f +42 +>>> f = 5 +>>> print example.get_value() +42 +>>> assert None is example.get_null_value() + </pre> + <p>Note that the value of the static C++-variable <code>val</code> is not changed when python assigns + a new value to <code>f</code>. This is caused by the logic of <code>return_pointee_value</code>: It returns + the <code>boost::python::object</code> which is constructed from <code>*val</code>.</p> + + <p><b>Example 2:</b> The following example uses return_pointee_value to return an object that is wrapped + with SWIG. I use the wxPython-API because it's easier to understand.</p> + + <p>In C++:</p> +<pre> +#include <boost/python/module.hpp> +#include <boost/python/class.hpp> +#include <boost/python/return_pointee_value.hpp> +#include <boost/python/return_value_policy.hpp> +#inlcude <wxPython.h> +#include <utility> + +// The following code assumes that there is set up a +// working wxWidgets application + +// Function to wrap: +wxWindow* getMainFrame() +{ + return wxGetApp().GetTopWindow(); +} + +// Custom conversion for wxWindow. +struct convert_wxWindow +{ + // Method that does the conversion. Note that it takes a reference to wxWindow, + // not a reference to a pointer. This is needed, because the return value policy + // converts the objects not the pointers. + static PyObject* convert(wxWindow const& o) + { + // Convert the pointer to wxWindow, instead of its value. This is how + // it should be done to convert wxObjects to wxPython. The object will be + // recognized well in python + PyObject* arg = wxPyMake_wxObject(const_cast<wxWindow*>(&o), false); + return arg; + } +}; + +// Wrapper code +using namespace boost::python; +BOOST_PYTHON_MODULE(MyApp) +{ + def("getMainFrame", getMainFrame, return_value_policy<return_pointee_value>()); + + // register the custom converter + // converter for wxWindow, its conversion_struct, false tells boost that + // we do not have a get_pytype() method in it. + to_python_converter<wxWindow, convert_wxWindow, false>(); +} +</pre> + In Python: The example assumes that you call the python function <code>doit</code> from C++: +<pre> +def doit(): + w = MyApp.getMainFrame() + w.Hide() + print "Haha, it's gone!" + w.Show() + return 0 + +</pre> +<p>Note that the returned wxWindow is the same object as the return value of <code>getMainFrame</code> because of +the custom construction for <code>boost::python::object</code> from wxWindow. The conversion just converts the pointer, +not the object itself.</p> + + <p>Revised + <!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan --> + 22 July, 2008 + <!--webbot bot="Timestamp" endspan i-checksum="39359" --> + </p> + + <p><i>© Copyright Maximilian Matthe, Roman Yakovenko 2006, 2008.</i></p> + </body> +</html> + Index: libs/python/test/return_pointee_value.cpp =================================================================== --- libs/python/test/return_pointee_value.cpp (revision 0) +++ libs/python/test/return_pointee_value.cpp (revision 0) @@ -0,0 +1,55 @@ +// Copyright Maximilian Matthe 2008. +// 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 <boost/python/return_pointee_value.hpp> +#include <boost/python/module.hpp> +#include <boost/python/return_value_policy.hpp> +#include <boost/python/def.hpp> +#include <boost/python/to_python_converter.hpp> + +// Out returned, unkown type +struct IntWrapper +{ + IntWrapper(int v) : val(v) {} + int val; +}; + +// the custom converter +struct convert_IntWrapper +{ + static PyObject* convert(IntWrapper const& w) + { + return boost::python::incref(boost::python::object(w.val).ptr()); + } +}; + +IntWrapper* returnIntWrapper() +{ + static IntWrapper w(42); + return &w; +} + +////////////////////////////////////////////////////////// +float* get_value() +{ + static float value = 0.5f; + return &value; +} + +float* get_null_value() +{ + return NULL; +} + +BOOST_PYTHON_MODULE(return_pointee_value) +{ + using namespace boost::python; + def("returnIntWrapper", returnIntWrapper, return_value_policy<return_pointee_value>()); + + def("get_value", get_value, return_value_policy<return_pointee_value>()); + def("get_null_value", get_null_value, return_value_policy<return_pointee_value>()); + + to_python_converter<IntWrapper, convert_IntWrapper, false>(); +} \ No newline at end of file Index: libs/python/test/return_pointee_value.py =================================================================== --- libs/python/test/return_pointee_value.py (revision 0) +++ libs/python/test/return_pointee_value.py (revision 0) @@ -0,0 +1,25 @@ +# Copyright Maximilian Matthe 2008 +# 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) + +import return_pointee_value + + +def run(args = None): + a = return_pointee_value.returnIntWrapper() + assert a == 42 + assert 0.5 == return_pointee_value.get_value() + assert None is return_pointee_value.get_null_value() + return 0 + +if __name__ == '__main__': + print 'running...' + import sys + status = run() + if(status == 0): + print "Succes..." + else: + print "Failure!" + sys.exit(status) + \ No newline at end of file

on Tue Jul 22 2008, Maximilian Matthe <Maxi.Matthe-AT-web.de> wrote:
Hi!
I've created a patch that adds a new return value policy to boost.python: return_pointee_value. Roman Yakovenko has "invented" it, I have added it to the current SVN trunk on my PC. I've written documentation for it and also some test cases. David Abrahams encouraged me to send a patch to the boost list, which I do now.
Thanks, but actually I was trying to suggest posting the patch in a ticket at http://svn.boost.org. -- Dave Abrahams BoostPro Computing http://www.boostpro.com
participants (2)
-
David Abrahams
-
Maximilian Matthe