Revision: 867
http://svn.sourceforge.net/pygccxml/?rev=867&view=rev
Author: roman_yakovenko
Date: 2007-01-15 14:11:57 -0800 (Mon, 15 Jan 2007)
Log Message:
-----------
replacing auto_conversion example with more interesting one
Modified Paths:
--------------
pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/auto_conversion.cpp
pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/test.py
Added Paths:
-----------
pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/tuple_conversion.hpp
Modified: pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/auto_conversion.cpp
===================================================================
--- pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/auto_conversion.cpp 2007-01-11 20:36:31 UTC (rev 866)
+++ pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/auto_conversion.cpp 2007-01-15 22:11:57 UTC (rev 867)
@@ -1,174 +1,71 @@
#include "boost/python.hpp"
-#include "boost/python/object.hpp" //len function
-#include "boost/python/ssize_t.hpp" //ssize_t type definition
-#include "boost/python/detail/none.hpp"
+#include "tuple_conversion.hpp"
+#include "boost/tuple/tuple_comparison.hpp"
//This example is very similar to one found in Boost.Python FAQs:
//How can I automatically convert my custom string type to and from a Python string?
//http://www.boost.org/libs/python/doc/v2/faq.html#custom_string
//I will introduce another example with detailed explanation.
-//Simple class, which describes a point in 3D. I don't want to export the class.
+//Simple class, which describes a record in 3D. I don't want to export the class.
//Instead of this I want Python user pass tuple as argument to all functions
-//that expects point3d_t class instance and Boost.Python will automaticly handle
+//that expects record_t class instance and Boost.Python will automaticly handle
//the conversion. Same conversion should be applied on functions, which returns
-//point3d_t class instance
+//record_t class instance
-struct point3d_t{
- explicit point3d_t(int x_=0, int y_=0, int z_=0)
- : x(x_), y(y_), z(z_)
- {}
-
- bool operator==( const point3d_t& other ) const{
- return x == other.x && y == other.y && z == other.z;
- }
-
- int x, y, z;
-};
+typedef boost::tuple< int, int, int > record_t;
-
-point3d_t point_ret_val_000() {
- return point3d_t();
+record_t record_ret_val_000() {
+ return record_t(0,0,0);
}
-point3d_t point_ret_val_101() {
- return point3d_t(1,0,1);
+record_t record_ret_val_101() {
+ return record_t(1,0,1);
}
-point3d_t& point_ret_ref_010(){
- static point3d_t pt( 0,1,0 );
+record_t& record_ret_ref_010(){
+ static record_t pt( 0,1,0 );
return pt;
}
-point3d_t* point_ret_ptr_110(){
- static point3d_t pt( 1,1,0 );
+record_t* record_ret_ptr_110(){
+ static record_t pt( 1,1,0 );
return &pt;
}
-bool test_point_val_000( point3d_t pt ){
- return pt == point3d_t( 0,0,0 );
+bool test_record_val_000( record_t pt ){
+ return pt == record_t( 0,0,0 );
}
-bool test_point_cref_010( const point3d_t& pt ){
- return pt == point3d_t( 0,1,0 );
+bool test_record_cref_010( const record_t& pt ){
+ return pt == record_t( 0,1,0 );
}
-bool test_point_ref_110( point3d_t& pt ){
- return pt == point3d_t( 1,1,0 );
+bool test_record_ref_110( record_t& pt ){
+ return pt == record_t( 1,1,0 );
}
-bool test_point_ptr_101( point3d_t* pt ){
- return pt && *pt == point3d_t( 1,0,1 );
+bool test_record_ptr_101( record_t* pt ){
+ return pt && *pt == record_t( 1,0,1 );
}
namespace bpl = boost::python;
-namespace point3d_conversion{
-
-struct to_tuple{
- //"To Python" conversion is pretty simple:
- //Using Python C API create an object and than manually fill it with data.
- //In this case Boost.Python provides a convenience API for tuple construction.
- //I don't see any reason not to use it.
- static PyObject* convert(point3d_t const& pt){
- return bpl::incref( bpl::make_tuple( pt.x, pt.y, pt.z ).ptr() );
- }
-};
-
-struct from_tuple{
- //"From Python" conversion is more complex. Memory managment is the main
- //reason for this.
-
- //The first step in conversion from the Python object is to check whether
- //the object is the right one.
- static void* convertible(PyObject* py_seq){
- if( !PySequence_Check( py_seq ) ){
- //check that the argument is a sequence
- return 0;
- }
- bpl::object seq = bpl::object( bpl::handle<>( bpl::borrowed( py_seq ) ) );
- bpl::ssize_t size = bpl::len( seq );
- if( 3 != size ){
- return 0;
- }
- for( bpl::ssize_t i = 0; i < 3; ++i ){
- //test that every item in sequence has int type
- bpl::object item = seq[i];
- bpl::extract<int> type_checker( item );
- if( !type_checker.check() ){
- return 0;
- }
- }
- return py_seq;
- }
-
- //rvalue_from_python_stage1_data is a class defined in
- //boost/python/converter/rvalue_from_python_data.hpp file. The file contains
- //nice and not "very" technical explanation about data management for rvalue
- //conversions from Python to C++ types.
-
- //In my words, the rvalue_from_python_stage1_data class contains memory
- //needed for construction of appropriate C++ object. The class also manages
- //the life time of the constructed C++ object.
-
- static void
- construct( PyObject* py_seq, bpl::converter::rvalue_from_python_stage1_data* data){
- if( !convertible( py_seq ) ){
- bpl::throw_error_already_set();
- }
-
- //At this point you are exposed to low level implementation details
- //of Boost.Python library. You use the reinterpret_cast, in order to get
- //access to the number of bytes, needed to store C++ object
- typedef bpl::converter::rvalue_from_python_storage<point3d_t> point3d_storage_t;
- point3d_storage_t* the_storage = reinterpret_cast<point3d_storage_t*>( data );
- //Now we need to get access to the memory, which will held the object.
- void* memory_chunk = the_storage->storage.bytes;
- //You should use placement new in order to create object in specific
- //location
- point3d_t* point = new (memory_chunk) point3d_t();
- //We allocated the memory, now we should tell Boost.Python to manage( free )
- //it later
- data->convertible = memory_chunk;
-
- bpl::object seq = bpl::object( bpl::handle<>( bpl::borrowed( py_seq ) ) );
- //Now I actually creates the object from the Python tuple
- bpl::object tmp = seq[0];
- point->x = bpl::extract< int >( tmp );
- tmp = seq[1];
- point->y = bpl::extract< int >( tmp );
- tmp = seq[2];
- point->z = bpl::extract< int >( tmp );
-
- }
-
-};
-
-void register_conversion(){
-
- bpl::to_python_converter< point3d_t, to_tuple>();
-
- bpl::converter::registry::push_back( &from_tuple::convertible
- , &from_tuple::construct
- , bpl::type_id<point3d_t>() );
-}
-
-}//namespace point3d_conversion
-
BOOST_PYTHON_MODULE( auto_conversion ){
- point3d_conversion::register_conversion();
- bpl::def("point_ret_val_000", &::point_ret_val_000);
- bpl::def("point_ret_val_101", &::point_ret_val_101);
- bpl::def("point_ret_ref_010"
- , &::point_ret_ref_010
+ bpl::register_tuple< record_t >();
+
+ bpl::def("record_ret_val_000", &::record_ret_val_000);
+ bpl::def("record_ret_val_101", &::record_ret_val_101);
+ bpl::def("record_ret_ref_010"
+ , &::record_ret_ref_010
, bpl::return_value_policy<bpl:: copy_non_const_reference>() );
- bpl::def( "point_ret_ptr_110"
- , &::point_ret_ptr_110
+ bpl::def( "record_ret_ptr_110"
+ , &::record_ret_ptr_110
, bpl::return_value_policy<bpl::return_by_value>() );
- bpl::def("test_point_val_000", &::test_point_val_000);
- bpl::def("test_point_cref_010", &::test_point_cref_010);
- bpl::def("test_point_ref_110", &::test_point_ref_110);
- bpl::def("test_point_ptr_101", &::test_point_ptr_101);
+ bpl::def("test_record_val_000", &::test_record_val_000);
+ bpl::def("test_record_cref_010", &::test_record_cref_010);
+ bpl::def("test_record_ref_110", &::test_record_ref_110);
+ bpl::def("test_record_ptr_101", &::test_record_ptr_101);
}
Modified: pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/test.py
===================================================================
--- pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/test.py 2007-01-11 20:36:31 UTC (rev 866)
+++ pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/test.py 2007-01-15 22:11:57 UTC (rev 867)
@@ -7,14 +7,14 @@
unittest.TestCase.__init__( self, *args )
def test_auto( self ):
- self.failUnless( (0,0,0) == auto_conv.point_ret_val_000() )
- self.failUnless( (1,0,1) == auto_conv.point_ret_val_101() )
- self.failUnless( (0,1,0) == auto_conv.point_ret_ref_010() )
- self.failUnlessRaises( TypeError, auto_conv.point_ret_ptr_110 )
- self.failUnless( auto_conv.test_point_val_000( (0,0,0) ) )
- self.failUnless( auto_conv.test_point_cref_010( (0,1,0) ) )
- self.failUnlessRaises( TypeError, auto_conv.test_point_ref_110, (1,1,0) )
- self.failUnlessRaises( TypeError, auto_conv.test_point_ptr_101, (1,0,1) )
+ self.failUnless( (0,0,0) == auto_conv.record_ret_val_000() )
+ self.failUnless( (1,0,1) == auto_conv.record_ret_val_101() )
+ self.failUnless( (0,1,0) == auto_conv.record_ret_ref_010() )
+ self.failUnlessRaises( TypeError, auto_conv.record_ret_ptr_110 )
+ self.failUnless( auto_conv.test_record_val_000( (0,0,0) ) )
+ self.failUnless( auto_conv.test_record_cref_010( (0,1,0) ) )
+ self.failUnlessRaises( TypeError, auto_conv.test_record_ref_110, (1,1,0) )
+ self.failUnlessRaises( TypeError, auto_conv.test_record_ptr_101, (1,0,1) )
def test_from_sequence( self ):
self.failUnless( from_conv.test_val_010( (0,1,0) ) )
Added: pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/tuple_conversion.hpp
===================================================================
--- pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/tuple_conversion.hpp (rev 0)
+++ pyplusplus_dev/docs/troubleshooting_guide/automatic_conversion/tuple_conversion.hpp 2007-01-15 22:11:57 UTC (rev 867)
@@ -0,0 +1,160 @@
+#ifndef tuple_conversion_hpp
+#define tuple_conversion_hpp
+
+#include "boost/python.hpp"
+#include "boost/tuple/tuple.hpp"
+#include "boost/python/object.hpp" //len function
+#include <boost/mpl/int.hpp>
+#include <boost/mpl/next.hpp>
+
+namespace boost{ namespace python{
+
+namespace details{
+//Small helper for incrementing index
+template< int index>
+typename mpl::next< mpl::int_< index > >::type increment_index(){
+ typedef typename mpl::next< mpl::int_< index > >::type next_index_type;
+ return next_index_type();
+}
+
+}
+
+//Conversion from C++ type to Python type is pretty simple. Basically, using
+//Python C API create an object and than initialize it.
+template< class TTuple >
+struct to_py_tuple{
+
+ typedef mpl::int_< tuples::length< TTuple >::value > length_type;
+
+ static PyObject* convert(const TTuple& c_tuple){
+ list values;
+ //add all c_tuple items to "values" list
+ convert_impl( c_tuple, values, mpl::int_< 0 >(), length_type() );
+ //create Python tuple from the list
+ return incref( python::tuple( values ).ptr() );
+ }
+
+private:
+
+ template< int index, int length >
+ static void
+ convert_impl( const TTuple &c_tuple, list& values, mpl::int_< index >, mpl::int_< length > ) {
+ values.append( c_tuple.template get< index >() );
+ convert_impl( c_tuple, values, details::increment_index<index>(), length_type() );
+ }
+
+ template< int index >
+ static void
+ convert_impl( const TTuple&, list& values, mpl::int_< index >, mpl::int_< index >)
+ {}
+
+};
+
+
+//Conversion from Python type to C++ type is a little bit complex. Boost.Python
+//library implements solution, which manages the memory of the allocated object.
+//In order to implement "from Python" conversion you should supply 2 functions.
+//The first one checks whether the conversion is possible. The second function
+//should construct an instance of the desired class.
+
+template< class TTuple>
+struct from_py_tuple{
+
+ typedef mpl::int_< tuples::length< TTuple >::value > length_type;
+
+ static void*
+ convertible(PyObject* py_obj){
+
+ if( !PyTuple_Check( py_obj ) ){
+ return 0;
+ }
+
+ python::tuple py_tuple( handle<>( borrowed( py_obj ) ) );
+ if( tuples::length< TTuple >::value != len( py_tuple ) ){
+ return 0;
+ }
+
+ if( convertible_impl( py_tuple, mpl::int_< 0 >(), length_type() ) ){
+ return py_obj;
+ }
+ else{
+ return 0;
+ }
+ }
+
+ static void
+ construct( PyObject* py_obj, converter::rvalue_from_python_stage1_data* data){
+ //At this point you are exposed to low level implementation details
+ //of Boost.Python library. You use the reinterpret_cast, in order to get
+ //access to the number of bytes, needed to store C++ object
+ typedef converter::rvalue_from_python_storage<TTuple> storage_t;
+ storage_t* the_storage = reinterpret_cast<storage_t*>( data );
+ //Now we need to get access to the memory, which will held the object.
+ void* memory_chunk = the_storage->storage.bytes;
+ //You should use placement new in order to create object in specific
+ //location
+ TTuple* c_tuple = new (memory_chunk) TTuple();
+ //We allocated the memory, now we should tell Boost.Python to manage( free )
+ //it later
+ data->convertible = memory_chunk;
+
+ python::tuple py_tuple( handle<>( borrowed( py_obj ) ) );
+ construct_impl( py_tuple, *c_tuple, mpl::int_< 0 >(), length_type() );
+ }
+
+private:
+
+ template< int index, int length >
+ static bool
+ convertible_impl( const python::tuple& py_tuple, mpl::int_< index >, mpl::int_< length > ){
+
+ typedef typename tuples::element< index, TTuple>::type element_type;
+
+ object element = py_tuple[index];
+ extract<element_type> type_checker( element );
+ if( !type_checker.check() ){
+ return false;
+ }
+ else{
+ return convertible_impl( py_tuple, details::increment_index<index>(), length_type() );
+ }
+ }
+
+ template< int index >
+ static bool
+ convertible_impl( const python::tuple& py_tuple, mpl::int_< index >, mpl::int_< index > ){
+ return true;
+ }
+
+ template< int index, int length >
+ static void
+ construct_impl( const python::tuple& py_tuple, TTuple& c_tuple, mpl::int_< index >, mpl::int_< length > ){
+
+ typedef typename tuples::element< index, TTuple>::type element_type;
+
+ object element = py_tuple[index];
+ c_tuple.template get< index >() = extract<element_type>( element );
+
+ construct_impl( py_tuple, c_tuple, details::increment_index<index>(), length_type() );
+ }
+
+ template< int index >
+ static void
+ construct_impl( const python::tuple& py_tuple, TTuple& c_tuple, mpl::int_< index >, mpl::int_< index > )
+ {}
+
+};
+
+template< class TTuple>
+void register_tuple(){
+
+ to_python_converter< TTuple, to_py_tuple<TTuple> >();
+
+ converter::registry::push_back( &from_py_tuple<TTuple>::convertible
+ , &from_py_tuple<TTuple>::construct
+ , type_id<TTuple>() );
+};
+
+} } //boost::python
+
+#endif//tuple_conversion_hpp
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|