Inserting code¶
Introduction¶
Py++ is not a magician! Sometimes there is a need to add code to generated file(s). This document will describe how you can insert your code to almost any place.
Insert code to module¶
Almost every Boost.Python
module has the following structure:
//declarations code
...
BOOST_PYTHON_MODULE(X)
{
//registrations code
...
}
Using module_builder_t
you can add code to declaration and registration
sections. More over you can add the code to head or tail of the section.
module_builder_t
class provides API, which will help you to complete the task:
add_declaration_code( self, code, tail=True )
This function will add a code to the declaration section. If you want to add the code to the head of the section, pass
tail=False
to the method.add_registration_code( self, code, tail=True )
This function will add a code to the registration section. If you want to add the code to the head of the section, pass
tail=False
to the method.
Example¶
mb = module_builder_t( ... )
mb.build_code_creator( ... )
mb.add_declaration_code( '//just a comment' )
mb.add_registration_code( '//another comment', False ) #adding code to the head
Insert code to class¶
class_t
declaration defines few methods, which add user code to the generated one.
Lets take a look on the following use case:
struct window_t{
...
void get_size( int& height, int& width ) const;
...
};
int
is immutable type in Python. So you cannot expose get_size
member
function as is. You need to create a wrapper and expose it.
In the near future Py++ will eliminate the need of creating hand written wrapper for this use case.
boost::python::tuple get_window_size( const window_t& win ){
int h(0), w(0);
win.get_size( h, w );
return boost::python::make_tuple( h, w );
}
Now you have to register it:
using boost::python;
class_< window_t >( ... )
.def( "get_size", &::get_window_size )
...
;
How it could be achieved with Py++? Class declaration, has also two functions:
add_declaration_code( self, code )
This method will add the code to the declaration section within the module.
If you split your module to few files, Py++ will add this code to the “cpp” file, class registration code will be written in.
Attention: there is no defined order between wrapper code and declaration section code. If you have dependencies between code from declaration section and class wrapper, consider to move declaration code to class wrapper.
add_registration_code( self, code, works_on_instance=True )
This method will add the code to the registration section of the class.
What is
works_on_instance
argument for? In our case, we added new method to the class. The first argument of the call will beself
.#From Python user can call this method like this: win = window_t( ) height, width = win.get_size()
If you will pass
works_on_instance=False
the following code will be generated:{ class_< window_t > window_exporter( "window_t" ); scope window_scope( window_exporter ); ... def( "get_size", &::get_window_size ); }
And in this case, user will be forced to pass reference to
window_t
object:win = window_t() height, width = window_t.get_size( win )
Example¶
mb = module_builder_t( ... )
window = mb.class_( 'window_t' )
window.add_declaration_code( get_window_size definition )
window.add_registration_code( 'def( "get_size", &::get_window_size )' )
#Py++ will add ';' if needed
Insert code to class wrapper¶
There are use cases, when you have to add code to class wrapper. Please take a look on the following thread: http://mail.python.org/pipermail/c++-sig/2006-June/010791.html .
The short description is the following: there are classes with parent/child relationship.
Parent keeps child class instances using raw pointer. When parent die, it also
destroys children classes. It is not an option to switch to boost::shared_ptr
.
The solution Niall Douglas found was to implement small lifetime manager. For this solution he needed:
- to add to every constructor of class wrapper some code that registers the instance of the class within the manager
- to add to destructor of class wrapper some code, that will destroy the instance if needed.
- to add to class wrapper new variable
Solution¶
def inject_code( cls ):
constructors = cls.constructors()
constructors.body = class instance registration code
#if you need to add code to default or copy constructor only
#than you can you the following shortcuts
cls.null_constructor_body = <<<your code>>>
cls.copy_constructor_body = <<<your code>>>
#which will update the appropriate ``body`` property.
#If you want to add code to the class destructor,
#use ``add_destructor_code`` method
cls.add_destructor_code( <<<your code>>> )
#If you need to add new class variables:
cls.add_wrapper_code( <<<variable declaration>>> )
mb = module_builder_t( ... )
for cls in mb.classes( <<<relevant classes only>>> ):
inject_code( cls )
Header files¶
Now, when you know how to add your code to a generated one, I think you also should now how to add your own set of include directives to the generated files. There are few ways to do this.
The easiest and the most effective one - tell to Py++ that generated code for the declaration should include additional files:
mb = module_builder_t( ... ) my_class = mb.class_( ... ) my_class.include_files.append( "vector" )
Every declaration has
include_files
property. This is a list of header files, you want to include from the generated file(s).Other approach is a little bit low level, but it allows you to add your header files to every generated file:
mb = module_builder_t( ... ) ... mb.build_code_creator( ... ) mb.code_creator.add_include( "iostream" )
You can also replace all (to be) generated header files with your own set:
mb.code_creator.replace_included_headers( ["stdafx.h"] )
Of course you can, and may be should, use both approaches.
I suggest you to spend some time and to tweak Py++ to generate source code with as little as possible include directives. This will save you huge amount of time later.