[pygccxml-commit] SF.net SVN: pygccxml:[1388] pyplusplus_dev
Brought to you by:
mbaas,
roman_yakovenko
From: <rom...@us...> - 2008-08-08 17:44:24
|
Revision: 1388 http://pygccxml.svn.sourceforge.net/pygccxml/?rev=1388&view=rev Author: roman_yakovenko Date: 2008-08-08 17:44:27 +0000 (Fri, 08 Aug 2008) Log Message: ----------- adding sizeof and some documentation Modified Paths: -------------- pyplusplus_dev/docs/documentation/functions/call_policies.rest pyplusplus_dev/pyplusplus/code_creators/__init__.py pyplusplus_dev/pyplusplus/code_creators/ctypes_integration_creators.py pyplusplus_dev/pyplusplus/code_repository/ctypes_integration.py pyplusplus_dev/pyplusplus/decl_wrappers/class_wrapper.py pyplusplus_dev/pyplusplus/module_creator/creator.py pyplusplus_dev/unittests/classes_tester.py pyplusplus_dev/unittests/data/classes_to_be_exported.hpp Added Paths: ----------- pyplusplus_dev/docs/documentation/ctypes/ctypes_integration.rest pyplusplus_dev/docs/documentation/ctypes/this_and_sizeof.rest pyplusplus_dev/docs/documentation/ctypes/unions.rest pyplusplus_dev/docs/documentation/ctypes/variables.rest pyplusplus_dev/docs/documentation/ctypes/www_configuration.py Added: pyplusplus_dev/docs/documentation/ctypes/ctypes_integration.rest =================================================================== --- pyplusplus_dev/docs/documentation/ctypes/ctypes_integration.rest (rev 0) +++ pyplusplus_dev/docs/documentation/ctypes/ctypes_integration.rest 2008-08-08 17:44:27 UTC (rev 1388) @@ -0,0 +1,94 @@ +================== +ctypes integration +================== + +.. contents:: Table of contents + +------------ +Introduction +------------ + +`Boost.Python`_ is really a very powerful library, but if you are working +with code written in plain "C" - you've got a problem. You have to create +wrappers for almost every function or variable. + +In general, if you want to work with plain "C" code from `Python`_ +you don't have to create any wrapper - you can use `ctypes`_ package. + +About ctypes +------------ +`ctypes`_ is a foreign function library for Python. It provides C +compatible data types, and allows to call functions in dlls/shared +libraries. It can be used to wrap these libraries in pure Python. + + +-------- +The idea +-------- + +The idea behind "ctypes integration" functionality is really simple: you +configure `Py++`_ to expose address of the variable\\return value, and than you +you use `ctypes`_ `from_address`_ functionality to access and modify the data. + +Obviously, this approach has pros and cons: + +* cons - it could be very dangerous - you can corrupt your application memory + +* cons - managing memory is not something a typical `Python`_ user get used to. + It is too "low level". + +* pros - you don't need to create wrapper in C++ + +* pros - a Python user has access to the data + +* pros - compilation time is smaller + +* pros - you still can create wrapper, but using `Python`_ + + +In my opinion, the better way to go is to "mix": + +1. expose your native code using `Boost.Python`_ and "ctypes integration" + functionality - it is easy and cheap + +2. use `ctypes`_ module to access your data + +3. create high level API in Python: the wrappers, which will ensure the + constraints and will provide more "natural" interface + +------------------------- +Implemented functionality +------------------------- + +`Py++`_ is able to + +* expose global and member variable address + +* expose "this" pointer value + +* expose a class "sizeof" + +* expose variable, which has a union type + +* return address of return value as integer - `new call policy was created`_ + +----------------- +Future directions +----------------- + +The functionality is going to be developed father and I intend to add +next features: + +* to port this functionality to 64bit systems + +* to add ability to expose "C" functions without using `Boost.Python`_. + +.. _`new call policy was created` : ./../../documentation/functions/call_policies.html#return-addressof +.. _`ctypes` : http://docs.python.org/lib/module-ctypes.html +.. _`from_address` : http://docs.python.org/lib/ctypes-data-types.html +.. _`Py++` : ./../../pyplusplus.html +.. _`pygccxml` : ./../../../pygccxml/pygccxml.html +.. _`Boost.Python`: http://www.boost.org/libs/python/doc/index.html +.. _`Python`: http://www.python.org +.. _`GCC-XML`: http://www.gccxml.org + Added: pyplusplus_dev/docs/documentation/ctypes/this_and_sizeof.rest =================================================================== --- pyplusplus_dev/docs/documentation/ctypes/this_and_sizeof.rest (rev 0) +++ pyplusplus_dev/docs/documentation/ctypes/this_and_sizeof.rest 2008-08-08 17:44:27 UTC (rev 1388) @@ -0,0 +1,47 @@ +============= +this & sizeof +============= + +.. contents:: Table of contents + +----------- +The purpose +----------- + +`Py++`_ can expose a class ``sizeof`` and ``this`` pointer value to `Python`_. +I created this functionality without special purpose in mind. + +------- +Example +------- + + .. code-block:: Python + + mb = module_builder_t( ... ) + cls = mb.class_( <your class> ) + cls.expose_this = True + cls.expose_sizeof = True + +The `Python`_ class will contain two properties ``this`` and ``sizeof``. The usage +is pretty simple: + + .. code-block:: Python + + import ctypes + import <your module> import <your class> as data_t + + d = data_t() + print d.this + print d.sizeof + + +Warning: I hope you know what you are doing, otherwise don't blame me :-) + +.. _`ctypes` : http://docs.python.org/lib/module-ctypes.html +.. _`from_address` : http://docs.python.org/lib/ctypes-data-types.html +.. _`Py++` : ./../../pyplusplus.html +.. _`pygccxml` : ./../../../pygccxml/pygccxml.html +.. _`Boost.Python`: http://www.boost.org/libs/python/doc/index.html +.. _`Python`: http://www.python.org +.. _`GCC-XML`: http://www.gccxml.org + Added: pyplusplus_dev/docs/documentation/ctypes/unions.rest =================================================================== --- pyplusplus_dev/docs/documentation/ctypes/unions.rest (rev 0) +++ pyplusplus_dev/docs/documentation/ctypes/unions.rest 2008-08-08 17:44:27 UTC (rev 1388) @@ -0,0 +1,75 @@ +========= +C++ union +========= + +.. contents:: Table of contents + +------------ +Introduction +------------ + +`Boost.Python`_ does not help you to expose a variable, which has a union type. +In this document, I am going to show you a complete example how to get access +to the data, stored in the variable. + +`Py++`_ will not expose a union - it is impossible using `Boost.Python`_, +instead it will expose the address of the variable and the rest is done from the +`Python`_ using `ctypes`_ package. + +-------- +Example +-------- + +For this example I am going to use next code: + + .. code-block:: C++ + + struct data_t{ + union actual_data_t{ + int i; + double d; + }; + actual_data_t data; + }; + +As in many other cases, `Py++`_ does the job automatically: + + .. code-block:: Python + + mb = module_builder_t( ... ) + mb.class_( 'data_t' ).include() + +no special code, to achieve the desired result, was written. + +The generated code is boring, so I will skip it and will continue to the usage +example: + + .. code-block:: Python + + import ctypes + import <your module> import data_t + + #lets define our union + class actual_data_t( ctypes.Union ): + _fields_ = [( "i", ctypes.c_int ), ( 'd', ctypes.c_double )] + + obj = data_t() + actual_data = actual_data_t.from_address( obj.data ) + #you can set\get data + actual_data.i = 18 + prit actual_data.i + actual_data.d = 12.12 + print actual_data.d + +That's all. Everything should work fine. You can add few getters and setters to +class ``data_t``, so you could verify the results. I did this for a tester, that +checks this functionality. + +.. _`ctypes` : http://docs.python.org/lib/module-ctypes.html +.. _`from_address` : http://docs.python.org/lib/ctypes-data-types.html +.. _`Py++` : ./../../pyplusplus.html +.. _`pygccxml` : ./../../../pygccxml/pygccxml.html +.. _`Boost.Python`: http://www.boost.org/libs/python/doc/index.html +.. _`Python`: http://www.python.org +.. _`GCC-XML`: http://www.gccxml.org + Added: pyplusplus_dev/docs/documentation/ctypes/variables.rest =================================================================== --- pyplusplus_dev/docs/documentation/ctypes/variables.rest (rev 0) +++ pyplusplus_dev/docs/documentation/ctypes/variables.rest 2008-08-08 17:44:27 UTC (rev 1388) @@ -0,0 +1,78 @@ +========= +Variables +========= + +.. contents:: Table of contents + +-------------- +expose_address +-------------- + +``variable_t`` declarations have got new property ``expose_address``. If you set +it value to ``True``, `Py++`_ will register new property with the same name, but +the type of it will be ``unsigned int`` and the value is address of the variable. + +`Py++`_ will take care and generate the right code for global, static and member +variables. + +---------------- +Show me the code +---------------- + +Lets say you have the following C++ code: + + .. code-block:: C++ + + struct bytes_t{ + bytes_t(){ + data = new int[5]; + for(int i=0; i<5; i++){ + data[i] = i; + } + } + ... + int* data; + static int* x; + }; + + //somewhere in a cpp file + int* bytes_t::x = new int( 1997 ); + +In order to get access to the ``bytes_t::data`` and ``bytes_t::x`` you +have to turn on ``expose_address`` property to ``True``: + + .. code-block:: Python + + mb = module_builder_t( ... ) + bytes = mb.class_( 'bytes_t' ) + bytes.vars().expose_address = True + +`Py++`_ will generate code, which will expose the address of the variables. + +and now it is a time to show some `ctypes`_ magic: + + .. code-block:: Python + + import ctypes + import your_module as m + + bytes = m.bytes_t() + + data_type = ctypes.POINTER( ctypes.c_int ) + data = data_type.from_address( bytes.data ) + for j in range(5): + print '%d : %d' % ( j, data[j] ) + + data_type = ctypes.POINTER( ctypes.c_int ) + data = data_type.from_address( m.bytes_t.x ) + print x.contents.value + + +.. _`ctypes` : http://docs.python.org/lib/module-ctypes.html +.. _`from_address` : http://docs.python.org/lib/ctypes-data-types.html +.. _`Py++` : ./../../pyplusplus.html +.. _`pygccxml` : ./../../../pygccxml/pygccxml.html +.. _`Boost.Python`: http://www.boost.org/libs/python/doc/index.html +.. _`Python`: http://www.python.org +.. _`GCC-XML`: http://www.gccxml.org + Added: pyplusplus_dev/docs/documentation/ctypes/www_configuration.py =================================================================== --- pyplusplus_dev/docs/documentation/ctypes/www_configuration.py (rev 0) +++ pyplusplus_dev/docs/documentation/ctypes/www_configuration.py 2008-08-08 17:44:27 UTC (rev 1388) @@ -0,0 +1,6 @@ +name = 'ctypes integration' +main_html_file = 'ctypes_integration.html' + +names = { 'ctypes_integration' : 'ctypes integration' + , 'this_and_sizeof' : 'this & sizeof'} + Modified: pyplusplus_dev/docs/documentation/functions/call_policies.rest =================================================================== --- pyplusplus_dev/docs/documentation/functions/call_policies.rest 2008-08-08 14:32:06 UTC (rev 1387) +++ pyplusplus_dev/docs/documentation/functions/call_policies.rest 2008-08-08 17:44:27 UTC (rev 1388) @@ -209,6 +209,57 @@ mb.free_function( ... ).call_policies \ = call_policies.custom_call_policies( your call policies code, "xyz.hpp" ) +return_addressof +---------------- + +Class ``return_addressof`` is a model of `ResultConverterGenerator`_ which +can be used to wrap C++ functions returning any pointer, such that the pointer +value is converted to ``unsigned int`` and it is copied into a new Python object. + +This call policy was created to be used with ``ctypes`` package and provide access +to some raw\\low level data, without creating wrappers. + +Pay attention: you have to manage the memory by your own. + +Example +~~~~~~~ + +.. code-block:: C++ + + int* get_value(){ + static int buffer[] = { 0,1,2,3,4 }; + return buffer; + } + + namespace bpl = boost::python; + BOOST_PYTHON_MODULE(my_module){ + def( "get_value" + , bpl::return_value_policy< pyplusplus::call_policies::return_addressof<> >() ); + } + +The `Py++`_ code is not that different from what you already know: + +.. code-block:: Python + + from pyplusplus import module_builder + from pyplusplus.module_builder import call_policies + + mb = module_builder.module_builder_t( ... ) + mb.free_function( return_type='float *' ).call_policies \ + = call_policies.return_value_policy( call_policies.return_addressof ) + +Python code: + +.. code-block:: Python + + import ctypes + import my_module + + buffer_type = ctypes.c_int * 5 + buffer = buffer_type.from_address( my_module.get_value() ) + assert [0,1,2,3,4] == list( buffer ) + + return_pointee_value -------------------- Modified: pyplusplus_dev/pyplusplus/code_creators/__init__.py =================================================================== --- pyplusplus_dev/pyplusplus/code_creators/__init__.py 2008-08-08 14:32:06 UTC (rev 1387) +++ pyplusplus_dev/pyplusplus/code_creators/__init__.py 2008-08-08 17:44:27 UTC (rev 1388) @@ -137,3 +137,4 @@ from properties import property_t from ctypes_integration_creators import expose_this_t +from ctypes_integration_creators import expose_sizeof_t Modified: pyplusplus_dev/pyplusplus/code_creators/ctypes_integration_creators.py =================================================================== --- pyplusplus_dev/pyplusplus/code_creators/ctypes_integration_creators.py 2008-08-08 14:32:06 UTC (rev 1387) +++ pyplusplus_dev/pyplusplus/code_creators/ctypes_integration_creators.py 2008-08-08 17:44:27 UTC (rev 1388) @@ -37,3 +37,19 @@ def _get_system_headers_impl( self ): return [code_repository.ctypes_integration.file_name] + +class expose_sizeof_t( registration_based.registration_based_t + , declaration_based.declaration_based_t ): + """ + creates code that expose address of the object to Python + """ + + def __init__(self, class_ ): + registration_based.registration_based_t.__init__( self ) + declaration_based.declaration_based_t.__init__( self, declaration=class_) + + def _create_impl(self): + return 'def( pyplus_conv::register_sizeof( boost::type< %s >() ) )' % self.decl_identifier + + def _get_system_headers_impl( self ): + return [code_repository.ctypes_integration.file_name] Modified: pyplusplus_dev/pyplusplus/code_repository/ctypes_integration.py =================================================================== --- pyplusplus_dev/pyplusplus/code_repository/ctypes_integration.py 2008-08-08 14:32:06 UTC (rev 1387) +++ pyplusplus_dev/pyplusplus/code_repository/ctypes_integration.py 2008-08-08 17:44:27 UTC (rev 1388) @@ -26,6 +26,7 @@ #include "boost/mpl/vector.hpp" #include "boost/function.hpp" #include "boost/cstdint.hpp" +#include "boost/type.hpp" #include "boost/bind.hpp" @@ -94,6 +95,29 @@ const char* m_name; }; + +class register_sizeof : public boost::python::def_visitor<register_sizeof> +{ + friend class boost::python::def_visitor_access; + +public: + + template< typename TType > + register_sizeof( boost::type< TType > ) + : m_sizeof( sizeof( TType ) ) + {} + + template <class classT> + void visit(classT& c) const{ + boost::python::scope cls_scope( c ); + cls_scope.attr("sizeof") = m_sizeof; + } + +private: + size_t m_sizeof; +}; + + } /*pyplusplus*/ } /*convenience*/ namespace pyplus_conv = pyplusplus::convenience; Modified: pyplusplus_dev/pyplusplus/decl_wrappers/class_wrapper.py =================================================================== --- pyplusplus_dev/pyplusplus/decl_wrappers/class_wrapper.py 2008-08-08 14:32:06 UTC (rev 1387) +++ pyplusplus_dev/pyplusplus/decl_wrappers/class_wrapper.py 2008-08-08 17:44:27 UTC (rev 1388) @@ -191,6 +191,7 @@ self._require_self_reference = False self._exposed_class_type = self.EXPOSED_CLASS_TYPE.DECLARED self._expose_this = None + self._expose_sizeof = None def _get_redefine_operators( self ): return self._redefine_operators @@ -636,6 +637,13 @@ expose_this = property( _get_expose_this, _set_expose_this , doc="boolean, if True an object address( this pointer ) will be exposed to Python as integer.") + def _get_expose_sizeof( self ): + return self._expose_sizeof + def _set_expose_sizeof( self, new_value ): + self._expose_sizeof = new_value + expose_sizeof = property( _get_expose_sizeof, _set_expose_sizeof + , doc="boolean, if True the sizeof(obj) will be exposed to Python as integer.") + @property def introduces_new_scope(self): """returns True, if during exposing this class, new scope will be created Modified: pyplusplus_dev/pyplusplus/module_creator/creator.py =================================================================== --- pyplusplus_dev/pyplusplus/module_creator/creator.py 2008-08-08 14:32:06 UTC (rev 1387) +++ pyplusplus_dev/pyplusplus/module_creator/creator.py 2008-08-08 17:44:27 UTC (rev 1388) @@ -592,6 +592,9 @@ if cls_decl.expose_this: cls_cc.adopt_creator( code_creators.expose_this_t( cls_decl ) ) + if cls_decl.expose_sizeof: + cls_cc.adopt_creator( code_creators.expose_sizeof_t( cls_decl ) ) + for decl in exportable_members: if decl in exposed: continue Modified: pyplusplus_dev/unittests/classes_tester.py =================================================================== --- pyplusplus_dev/unittests/classes_tester.py 2008-08-08 14:32:06 UTC (rev 1387) +++ pyplusplus_dev/unittests/classes_tester.py 2008-08-08 17:44:27 UTC (rev 1388) @@ -19,6 +19,7 @@ def customize(self, mb ): mb.classes().expose_this = True + mb.classes().expose_sizeof = True mb.class_( 'fundamental2' ).alias = 'FUNDAMENTAL2' apple = mb.class_( 'apple' ) self.failUnless( apple.alias == 'the_tastest_fruit' ) @@ -53,6 +54,7 @@ self.failUnless( -24 == module.protected_static_t().invert_sign(24) ) self.failUnless( 67 == module.protected_static_t().invert_sign(-67) ) self.failUnless( module.protected_static_t().this ) + self.failUnless( module.protected_static_t().sizeof ) def create_suite(): suite = unittest.TestSuite() Modified: pyplusplus_dev/unittests/data/classes_to_be_exported.hpp =================================================================== --- pyplusplus_dev/unittests/data/classes_to_be_exported.hpp 2008-08-08 14:32:06 UTC (rev 1387) +++ pyplusplus_dev/unittests/data/classes_to_be_exported.hpp 2008-08-08 17:44:27 UTC (rev 1388) @@ -38,7 +38,7 @@ namespace abstracts{ class abstract{ -public: +public: abstract(){} abstract(int){} abstract(int, double, const abstract&){} @@ -48,13 +48,13 @@ virtual bool overloaded_virtual(){ return true;} virtual bool overloaded_virtual(int){ return true;} - + virtual int overloaded_pure_virtual(int) const = 0; virtual void overloaded_pure_virtual(double) const = 0; - + virtual int some_virtual(){ return 1; } -}; - +}; + } namespace constructors{ @@ -64,11 +64,11 @@ constructor1( const constructor1& ){} constructor1(int x, int y){} constructor1(int y, const constructor1& x ){} - + constructor1( const double ){} struct internal_data{}; constructor1( const internal_data ){} -}; +}; } @@ -76,15 +76,17 @@ struct scope_based_exposer{ enum EColor{ red, green, blue }; scope_based_exposer(EColor c=blue){} -}; +}; } namespace protected_static{ class protected_static_t{ protected: static int identity(int x){ return x; } - + int invert_sign(int x){ return -1*x; } +private: + int i; }; }; @@ -104,15 +106,15 @@ }; }; -}//classes +}//classes namespace pyplusplus{ namespace aliases{ - + typedef classes::hierarchical::apple the_tastest_fruit; - + typedef classes::protected_static::protected_static_t PROTECTED_STATIC_T_1; typedef classes::protected_static::protected_static_t PROTECTED_STATIC_T_2; - + }} #endif//__classes_to_be_exported_hpp__ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |