[pygccxml-commit] SF.net SVN: pygccxml: [1220] pyplusplus_dev/docs/documentation
Brought to you by:
mbaas,
roman_yakovenko
From: <rom...@us...> - 2008-01-05 21:19:41
|
Revision: 1220 http://pygccxml.svn.sourceforge.net/pygccxml/?rev=1220&view=rev Author: roman_yakovenko Date: 2008-01-05 13:19:44 -0800 (Sat, 05 Jan 2008) Log Message: ----------- moving docs Removed Paths: ------------- pyplusplus_dev/docs/documentation/best_practices.rest pyplusplus_dev/docs/documentation/hints.rest pyplusplus_dev/docs/documentation/how_to.rest Deleted: pyplusplus_dev/docs/documentation/best_practices.rest =================================================================== --- pyplusplus_dev/docs/documentation/best_practices.rest 2008-01-05 21:18:15 UTC (rev 1219) +++ pyplusplus_dev/docs/documentation/best_practices.rest 2008-01-05 21:19:44 UTC (rev 1220) @@ -1,176 +0,0 @@ -============== -Best practices -============== - -.. contents:: Table of contents - ------------- -Introduction ------------- - -`Py++`_ has reach interface and a lot of functionality. Sometimes reach -interface helps, but sometimes it can confuse. This document will describe how -effectively to use `Py++`_. - ------------- -Big projects ------------- - -Definition ----------- - -First of all, let me to define "big project". "Big project" is a project with -few hundred of header files. `Py++`_ was born to create `Python`_ bindings -for such projects. If you take a look `here`__ you will find few such projects. - -.. __ : ./../../pyplusplus/quotes.html - -Tips ----- - -* Create one header file, which will include all project header files. - - Doing it this way makes it so `GCC-XML`_ is only called once and it reduces the - overhead that would occur if you pass `GCC-XML`_ all the files individually. - Namely `GCC-XML`_ would have to run hundreds of times and each call would - actually end up including quite a bit of common code anyway. This way takes a - `GCC-XML`_ processing time from multiple hours with gigabytes of caches to a - couple minutes with a reasonable cache size. - - You can read more about different caches supported by `pygccxml`_ `here`__. - ``module_builder_t.__init__`` method takes reference to an instance of cache - class or ``None``: - - .. code-block:: Python - - from module_builder import * - mb = module_builder_t( ..., cache=file_cache_t( path to project cache file ), ... ) - -* Single header file, will also improve performance compiling the generated bindings. - - When `Py++`_ generated the bindings, you have a lot of .cpp files to - compile. The project you are working on is big. I am sure it takes a lot of - time to compile projects that depend on it. Generated code also depend on it, - more over this code contains a lot of template instantiations. So it could - take a great deal of time to compile it. Allen Bierbaum investigated this - problem. He found out that most of the time is really spent processing all the - headers, templates, macros from the project and from the boost library. So he - come to conclusion, that in order to improve compilation speed, user should - be able to control( to be able to generate ) precompiled header file. He - implemented an initial version of the functionality. After small discussion, - we agreed on next interface: - - .. code-block:: Python - - class module_builder_t( ... ): - ... - def split_module( self, directory_path, huge_classes=None, precompiled_header=None ): - ... - - ``precompiled_header`` argument could be ``None`` or string, that contains - name of precompiled header file, which will be created in the directory. - `Py++`_ will add to it header files from `Boost.Python`_ library and - your header files. - - What is ``huge_classes`` argument for? ``huge_classes`` could be ``None`` or - list of references to class declarations. It is there to provide a solution to - `this error`_. `Py++`_ will automatically split generated code for the - huge classes to few files: - - .. code-block:: Python - - mb = module_builder_t( ... ) - ... - my_big_class = mb.class_( my_big_class ) - mb.split_module( ..., huge_classes=[my_big_class], ... ) - - * **Caveats** - - Consider next file layout: - :: - - boost/ - date_time/ - ptime.hpp - time_duration.hpp - date_time.hpp //main header, which include all other header files - - Py++ currently does not handle relative paths as input very well, so it is - recommended that you use "os.path.abspath()" to transform the header file to - be processed into an absolute path: - - .. code-block:: Python - - #Next code will expose nothing - mb = module_builder( [ 'date_time/date_time.hpp' ], ... ) - - #while this one will work as expected - import os - mb = module_builder( [ os.path.abspath('date_time/date_time.hpp') ], ... ) - -.. _`this error` : http://boost.org/libs/python/doc/v2/faq.html#c1204 -.. __ : ./../../pygccxml/design.html - -* Keep the declaration tree small. - - When parsing the header files to build the declaration tree, there will also - be the occasional "junk" declaration inside the tree that is not relevant to - the bindings you want to generate. These extra declarations come from header - files that were included somewhere in the header files that you were actually - parsing (e.g. if that library uses the STL or OpenGL or other system headers - then the final declaration tree will contain those declarations, too). - It can happen that the majority of declarations in your declaration tree are - such "junk" declarations that are not required for generating your bindings - and that just slow down the generation process (reading the declaration cache - and doing queries will take longer). - - To speed up your generation process you might want to consider making the - declaration tree as small as possible and only store those declarations that - somehow have an influence on the bindings. Ideally, this is done as early - as possible and luckily gccxml provides an option that allows you to reduce - the number of declarations that it will store in the output XML file. You can - specify one or more declarations using the ``-fxml-start`` option and only - those sub-tree starting at the specified declarations will be written. For - example, if you specify the name of a particular class, only this class - and all its members will get written. Or if your project already uses - a dedicated namespace you can simply use this namespace as a starting point - and all declarations stemming from system headers will be ignored (except - for those declarations that are actually used within your library). - - In the ``pygccxml`` package you can set the value for the ``-fxml-start`` - option using the ``start_with_declarations`` attribute of the - ``pygccxml.parser.config_t`` object that you are passing to the parser. - -* Use `Py++`_ repository of generated files md5 sum. - - `Py++`_ is able to store md5 sum of generated files in a file. Next time you - will generate code, `Py++`_ will compare generated file content against the sum, - instead of loading the content of the previously generated file from the disk - and comparing against it. - - .. code-block:: Python - - mb = module_builder_t( ... ) - ... - my_big_class = mb.class_( my_big_class ) - mb.split_module( ..., use_files_sum_repository=True ) - - `Py++`_ will generate file named "<your module name>.md5.sum" in the directory - it will generate all the files. - - Enabling this functionality should give you 10-15% of performance boost. - - * **Caveats** - - If you changed manually some of the files - don't forget to delete the relevant - line from "md5.sum" file. You can also delete the whole file. If the file is - missing, `Py++`_ will use old plain method of comparing content of the files. - It will not re-write "unchanged" files and you will not be forced to recompile - the whole project. - - -.. _`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 Deleted: pyplusplus_dev/docs/documentation/hints.rest =================================================================== --- pyplusplus_dev/docs/documentation/hints.rest 2008-01-05 21:18:15 UTC (rev 1219) +++ pyplusplus_dev/docs/documentation/hints.rest 2008-01-05 21:19:44 UTC (rev 1220) @@ -1,64 +0,0 @@ -===== -Hints -===== - -.. contents:: Table of contents - ----------------------------------- -Class template instantiation alias ----------------------------------- - -`Py++`_ has nice feature. If you define ``typedef`` for instantiated class -template, than `Py++`_ will use it as a `Python`_ class name. - -For example: - -.. code-block:: C++ - - #include <vector> - typedef std::vector< int > numbers; - numbers generate_n(){ - ... - } - -`Py++`_ will use "numbers" as Python class name: - -.. code-block:: C++ - - using boost::python; - class_< std::vector< int > >( "numbers" ) - ... - ; - -`Py++`_ will pick up the alias, only in case the class has single "typedef". - -``pyplusplus::aliases`` namespace ---------------------------------- - -The previous approach is "implicit" - `Py++`_ does something behind the scene. -Recently (version 0.8.6 ), another approach was introduced: - -.. code-block:: C++ - - #include <vector> - - namespace pyplusplus{ namespace aliases{ - //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - typedef std::vector< int > numbers; - - } } //pyplusplus::aliases - -The idea is that you create namespace with a special name - ``pyplusplus::aliases`` -and `Py++`_ automatically picks the class aliases from it. In case you accidentally -introduced two or more different aliases to the same class, it will pick the -longest one and print a warning. Other advantages of the approach: - -* you are not forced to learn new API - -* you continue to use your favorite editor and familiar language - -.. _`Py++` : ./../pyplusplus.html -.. _`Boost.Python`: http://www.boost.org/libs/python/doc/index.html -.. _`Python`: http://www.python.org -.. _`GCC-XML`: http://www.gccxml.org - Deleted: pyplusplus_dev/docs/documentation/how_to.rest =================================================================== --- pyplusplus_dev/docs/documentation/how_to.rest 2008-01-05 21:18:15 UTC (rev 1219) +++ pyplusplus_dev/docs/documentation/how_to.rest 2008-01-05 21:19:44 UTC (rev 1220) @@ -1,411 +0,0 @@ -============ -How to ... ? -============ - -.. contents:: Table of contents - ----------------------------------------- -How to add custom exception translation? ----------------------------------------- - -.. code-block:: C++ - - struct my_exception{ - ... - const std::string& error() const; - } - -First of all lets define ``translate`` function: - -.. code-block:: Python - - translate_code = \ - """ - void translate(const my_exception &exception){ - PyErr_SetString( PyExc_RuntimeError, exception.error().c_str() ); - } - """ - -.. code-block:: Python - - mb = module_builder_t( ... ) - mb.add_declaration_code( translate_code ) - - -Now we should register it: - -.. code-block:: Python - - registration_code = "boost::python::register_exception_translator<my_exception>(&translate);" - mb.add_registration_code( registration_code ) - -Small usage advice. -------------------- - -`Py++`_ allows you to define a query that will return you all exception classes: - -.. code-block:: Python - - mb = module_builder_t( ... ) - exception_classes = mb.decls( lambda decl: decl.name.endswith( 'exception' ) ) - -Now you can iterate on ``exception_classes``, generate and register translate -code for every class. - -That's all. - -------------------------------------------------------- -How to expose function, which has hand-written wrapper? -------------------------------------------------------- -.. code-block:: C++ - - struct window_t{ - ... - void get_size( int& height, int& widht ) const; - }; - -You can not expose ``get_size`` function as is - ``int`` is immutable type in -Python. So, we need to create a wrapper to the function: - -.. code-block:: C++ - - boost::python::tuple get_size_wrapper( const window_t& win ){ - int height(0), width( 0 ); - win.get_size( height, widht ); - return boost::python::make_tuple( height, width ); - } - -.. code-block:: C++ - - class_<window_t>( ... ) - .def( "get_size", &get_size_wrapper ) - ... - ; - -Now, after you know how this problem is solved. I will show how this solution -could be integrated with `Py++`_. - -.. code-block:: Python - - wrapper_code = \ - """ - static boost::python::tuple get_size( const window_t& win ){ - int height(0), width( 0 ); - win.get_size( height, width ); - return boost::python::make_tuple( height, width ); - } - """ - -.. code-block:: Python - - registration_code = 'def( "get_size", &%s::get_size )' % window.wrapper_alias - -.. code-block:: Python - - mb = module_builder_t( ... ) - window = mb.class_( "window_t" ) - window.member_function( "get_size" ).exclude() - window.add_wrapper_code( wrapper_code ) - window.registration_code( registration_code ) - -That's all. - -------------------------------------------------------------- -Fatal error C1204:Compiler limit: internal structure overflow -------------------------------------------------------------- - -If you get this error, than the generated file is too big. You will have to split -it to few files. Well, not you but `Py++`_, you will only have to tell it to do -that. - -If you are using ``module_builder_t.write_module`` method, consider to switch -to ``module_builder_t.split_module``. - -If you are using ``split_module``, but still the generated code for some class -could not be compiled, because of the error, you can ask `Py++`_ to split the -code generated for class to be split to few cpp files. - -For more information, please read the documentation. - ------------------------------------------------------- -How to automatically export template functions\\class? ------------------------------------------------------- - -Lets say you have next C++ function: - -.. code-block:: C++ - - // file point.h - namespace geometry{ - template< class T> - struct point_t{ - T x, y; - }; - template <class T> - double distance( const point_t<T>& point ){ - return sqrt( point.x * point.x + point.y*point.y ); - } - } //namespace geometry - -You should understand, that you can not export template itself, but only its -instantiations. The solution is built using next facts: - -* ``sizeof( class name )`` causes a compiler to instantiate the class - -* free function invocation causes a compiler to instantiate the function - -Lets say that we need to export the class and the function template -instantiations for ``int`` and ``custom_type`` types. There are few ways to do it. - -Simple and straightforward --------------------------- - -Open your favourite editor and create a header file with the content: - -.. code-block:: C++ - - #include "point.h" - -.. code-block:: C++ - - namespace py_details{ - inline void instantiate(){ - using namespace geometry; - sizeof( point_t<int> ); - sizeof( point_t<custom_type> ); - distance( point_t<int>(0,0) ); - distance( point_t<custom_type>(0,0) ); - } - } - -Now, you add this file to the list of files you pass as input to -``module_builder_t.__init__`` method and excludes the ``py_details`` namespace -declarations from being exported: - -.. code-block:: Python - - mb = module_builder_t( [..., just created file ], ... ) - mb.namespace( 'py_details' ).exclude() - -"Dynamic" instantiation ------------------------ - -Lets say you are less lucky than I, and you have to create ``X`` instantiations -of the class\\function. Obviously, the previous approach will not work for you. -The solution is to build your own code generator, which will generate code similar -to the one, in the previous paragraph. - -.. code-block:: Python - - from module_builder import module_builder_t, create_text_fc - -.. code-block:: Python - - def generate_instantiations_string( ... ): - ... - -.. code-block:: Python - - code = generate_instantiations_string( ... ) - -.. code-block:: Python - - mb = module_builder_t( [ ..., create_text_fc( code ) ], ... ) - mb.namespace( 'py_details' ).exclude() - - -`Py++`_ allows you to extract declarations from string, which contains -valid C++ code. It creates temporal header file and compiles it. At the end of -the compilation process it will remove it. You can read mode about ``create_text_fc`` -function `here`__. - -.. __ : ./../../pygccxml/design.html#parser-configuration-classes - - - -I understand that the provided solution is not perfect. I understand that something -better and simpler should be done, but a priority of this is low. There are few -tasks, that have much higher priority. Allen Bierbaum wants to fix the situation. -He created a `wiki page`_, that discuss possible solutions. Your contribution is -welcome too! - -.. _`wiki page` : https://realityforge.vrsource.org/view/PyppApi/TemplateSupport - ---------------------------------------------------- -How to deal with template on return type functions? ---------------------------------------------------- -.. code-block:: C++ - - struct environment_t{ - ... - template< class T> - T get_value(const std::string& name); - ... - }; - - //Somewhere in your code - environment_t env; - std::string path = env.get_value< std::string >( "PATH" ); - int version = env.get_value< int >( "VERSION" ); - -`GCC-XML`_ will see both instantiations, ``get_value<int>`` and ``get_value< std::string >``, -and will provide information about them. The name of both member functions, -reported by `GCC-XML`_ is "**get_value**". - -In this case, `Py++`_ generates a code, which contains few errors and if your are -lucky, depends on the compiler you use, the generated code will not compile. -Otherwise, you will discover the errors while testing the bindings. - -Generated code: - -.. code-block:: C++ - - bp::class_< environment_t >( "environment_t" ) - ... - .def( "get_value" - , (int ( ::environment_t::* )( ::std::string const & ) )( &::environment_t::get_value ) ) - .def( "get_value" - , (::std::string ( ::environment_t::* )( ::std::string const & ) )( &::environment_t::get_value ) ); - -The correct code: - -.. code-block:: C++ - - bp::class_< environment_t >( "environment_t" ) - .def( "get_value" - , (int ( ::environment_t::* )( ::std::string const & ) )( &::environment_t::get_value< int > ) ) - //--------------------------------------------------------------------------^^^^^^^^^^^^^^^^ - .def( "get_value" - , (::std::string ( ::environment_t::* )( ::std::string const & ) )( &::environment_t::get_value< std::string > ) ); - //------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^ - -The perfect one: - -.. code-block:: C++ - - bp::class_< environment_t >( "environment_t" ) - ... - .def( "get_value", &::environment_t::get_value< int > ) - .def( "get_value", &::environment_t::get_value< std::string > ); - -Solution --------- -.. code-block:: Python - - mb = module_builder_t( ..., optimize_queries=False, ... ) - environment = mb.class_( "environment_t" ) - for f in environment.member_functions( "get_value" ): - f.alias = f.name + "_" + f.return_type.decl_string #create new function alias - f.name = f.demangled_name - mb.run_query_optimizer() - -Before you read the rest of the answer, you should understand what is "name mangling" -means. If you don't, consider reading about it on `Wikipedia`__. - -.. __ : http://en.wikipedia.org/wiki/Name_mangling - - -The solution is pretty simple. `GCC-XML`_ reports mangled and demangled function -names. The demangled function name contains "real" function name: -``get_value< used type >``. You only have to instruct `Py++`_ to use it. - -`Py++`_ does not use by default demangled function name for mainly one reason. -Demangled function name is a string that contains a lot of information. `Py++`_ -implements a parser, which extracts the only relevant one. The parser -implementation is a little bit complex and was not heavily tested. By "heavily" I -mean that I tested it on a lot of crazy use cases and on a real project, but -there is always some new use case out there. I am almost sure it will work for -you. The problem, we deal with, is rare, so by default "demangled_name" -feature is turned off. - -By the way, almost the same problem exists for template classes. But, in the -classes use case `Py++`_ uses demangled name by default. - - -------------------------------------- -Py++ generated file name is too long! -------------------------------------- -There are use cases, when `Py++`_ generated file name is too long. In some cases -the code generation process even fails because of this. What can you do in order -to eliminate the problem? - -First of all the problem arises when you expose template instantiated classes -and you did not set the class alias. - -Let me explain: - -.. code-block:: C++ - - template < class T> - struct holder{ ... }; - - -Lets say that you want to export ``holder< int >`` class. Class name in `Python`_ -has few `constraints`_. `Py++`_ is aware of the `constraints`_ and if you didn't -set an alias to the class, `Py++`_ will do it for you. In this case, -"holder_less_int_grate\_" is the generated alias. Obviously it is much longer -and not readable. - -.. _`constraints` : http://www.python.org/doc/current/ref/identifiers.html - -There are few pretty good reasons for this behavior: - -* when you just start to work on `Python`_ bindings concentrate your attention - on really important things - -* if you forgot to set the class alias your users still can use the class - functionality, however the class name will be a little bit ugly. - - -In this case the generate alias for ``holder`` instantiation is relatively short. -Imagine how long it could be for ``std::map`` instantiation. - -`Py++`_ uses class alias for the file name. So if you want to force `Py++`_ to -generate files with short name, you have to set class alias: - -.. code-block:: Python - - from pyplusplus import module_builder - - mb = module_builder_t( ... ) - holder = mb.class_( 'holder< int >' ) - holder.alias = 'IntHolder' - #next line has same effect as the previous one: - holder.rename( 'IntHolder' ) - -The nice thing about this approach is that now `Python`_ users have "normal" -class name and you have short file name. - -------------------- -Full\relative paths -------------------- - -Consider next file layout: -:: - - boost/ - date_time/ - ptime.hpp - time_duration.hpp - date_time.hpp //main header, which include all other header files - -Py++ currently does not handle relative paths as input very well, so it is -recommended that you use ``os.path.abspath()`` to transform the header file to -be processed into an absolute path: - -.. code-block:: Python - - #Next code will expose nothing - mb = module_builder( [ 'date_time/date_time.hpp' ], ... ) - mb.split_module( ... ) - - #while this one will work as expected - import os - mb = module_builder( [ os.path.abspath('date_time/date_time.hpp') ], ... ) - mb.split_module( ... ) - - -.. _`Py++` : ./../pyplusplus.html -.. _`Boost.Python`: http://www.boost.org/libs/python/doc/index.html -.. _`Python`: http://www.python.org -.. _`GCC-XML`: http://www.gccxml.org This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |